* Remove Magellan serial and SD formats. Two different polls (one on the list, one in groundspeak forums) have confirmed disuse.
# MINIMAL_FMTS
set(MINIMAL_FMTS
- explorist_ini.cc
garmin.cc
garmin_device_xml.cc
garmin_tables.cc
geo.cc
gpx.cc
kml.cc
- magproto.cc
nmea.cc
wbt-200.cc
)
defs.h
dg-100.h
exif.h
- explorist_ini.h
filter.h
filter_vecs.h
format.h
kml.h
legacyformat.h
lowranceusr.h
- magellan.h
navilink.h
nmea.h
osm.h
arc
batch
bend
- classic-1
classic-2
classic-3
dg100
kml-read
kml
lowranceusr
- magellan_sd
- magellan
mtk
multiurlgpx
navilink
--- /dev/null
+#include "defs.h"
+#include "explorist_ini.h"
+#include "inifile.h"
+
+static inifile_t* inifile;
+static const char myname[] = "explorist";
+
+#ifdef DEAD_CODE_IS_REBORN
+static const char*
+explorist_read_value(const char* section, const char* key)
+{
+ return inifile_readstr(inifile, section, key);
+}
+#endif
+
+static mag_info*
+explorist_ini_try(const char* path)
+{
+ char* inipath;
+
+ xasprintf(&inipath, "%s/%s", path, "APP/Atlas.ini");
+ inifile = inifile_init(QString::fromUtf8(inipath), myname);
+ if (!inifile) {
+ xfree(inipath);
+ return nullptr;
+ }
+
+ auto* info = (mag_info*) xmalloc(sizeof(mag_info));
+ info->geo_path = nullptr;
+ info->track_path = nullptr;
+ info->waypoint_path = nullptr;
+
+ QString s = inifile_readstr(inifile, "UGDS", "WpFolder");
+ if (!s.isNull()) {
+ s.replace('\\', '/');
+ xasprintf(&info->waypoint_path, "%s/%s", path, CSTR(s));
+ }
+ s = inifile_readstr(inifile, "UGDS", "GcFolder");
+ if (!s.isNull()) {
+ s.replace('\\', '/');
+ xasprintf(&info->geo_path, "%s/%s", path, CSTR(s));
+ }
+ s = inifile_readstr(inifile, "UGDS", "TrkFolder");
+ if (!s.isNull()) {
+ s.replace('\\', '/');
+ xasprintf(&info->track_path, "%s/%s", path, CSTR(s));
+ }
+
+ inifile_done(inifile);
+ xfree(inipath);
+ return info;
+}
+
+mag_info*
+explorist_ini_get(const char** dirlist)
+{
+ mag_info* r = nullptr;
+ while (dirlist && *dirlist) {
+ r = explorist_ini_try(*dirlist);
+ if (r) {
+ return r;
+ }
+ }
+ return r;
+}
+
+void
+explorist_ini_done(mag_info* info)
+{
+ xfree(info->geo_path);
+ xfree(info->track_path);
+ xfree(info->waypoint_path);
+ xfree(info);
+}
--- /dev/null
+
+/*
+ * Interesting traits of the device from the *.ini files.
+ */
+struct mag_info {
+ char* geo_path;
+ char* track_path;
+ char* waypoint_path;
+};
+
+mag_info* explorist_ini_get(const char** directory_list);
+void explorist_ini_done(mag_info* info);
--- /dev/null
+/*
+ Copyright (C) 2002-2005 Robert Lipe, robertlipe@usa.net
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ */
+
+#ifndef MAGELLAN_H_INCLUDED_
+#define MAGELLAN_H_INCLUDED_
+
+#include <QString>
+#include "defs.h"
+
+/*
+ * Table of "interesting" Magellan models.
+ * Selfishly, if I haven't heard of it, it's not in the table.
+ * This doesn't mean I actually have TRIED all models listed below.
+ * (Donations welcome. :-)
+ */
+enum meridian_model {
+ mm_unknown = 0 ,
+ mm_gps315320,
+ mm_map410,
+ mm_map330,
+ mm_gps310,
+ mm_meridian,
+ mm_sportrak
+};
+
+struct pid_to_model_t {
+ meridian_model model;
+ int pid;
+ const char* model_n;
+};
+
+struct magellan_icon_mapping_t {
+ const char* token;
+ const char* icon;
+};
+
+QString mag_find_descr_from_token(const char* token);
+QString mag_find_token_from_descr(const QString& icon);
+
+unsigned int mag_checksum(const char*buf);
+QString m330_cleanse(const char* istring);
+
+Waypoint* mag_trkparse(char* trkmsg);
+void mag_rteparse(char* rtemsg);
+
+#endif // MAGELLAN_H_INCLUDED_
--- /dev/null
+/*
+ Communicate Thales/Magellan serial protocol.
+
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007,
+ 2008, 2010 Robert Lipe, robertlipe+source@gpsbabel.org
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ */
+
+#include <cassert> // for assert
+#include <cctype> // for isprint, toupper
+#include <cmath> // for fabs, lround
+#include <cstdio> // for sscanf, size_t
+#include <cstdlib> // for strtod, strtoul
+#include <cstring> // for strchr, strncmp, strlen, memmove, strrchr, memset
+
+#include <QByteArray> // for QByteArray
+#include <QDateTime> // for QDateTime
+#include <QDir> // for QDir, operator|, QDir::Files, QDir::Name, QDir::Readable
+#include <QFileInfo> // for QFileInfo
+#include <QFileInfoList> // for QFileInfoList
+#include <QLatin1String> // for QLatin1String
+#include <QList> // for QList
+#include <QScopedPointer> // for QScopedPointer
+#include <QString> // for QString, operator==
+#include <QStringList> // for QStringList
+#include <QTime> // for QTime
+#include <QVector> // for QVector
+#include <Qt> // for CaseInsensitive
+#include <QtGlobal> // for qPrintable, foreach
+
+#include "defs.h"
+#include "explorist_ini.h" // for explorist_ini_done, explorist_ini_get, mag_info
+#include "format.h" // for Format
+#include "gbfile.h" // for gbfclose, gbfeof, gbfgets, gbfopen, gbfwrite, gbfile
+#include "gbser.h" // for gbser_deinit, gbser_init, gbser_is_serial, gbser_read_line, gbser_set_port, gbser_write, gbser_OK
+#include "magellan.h" // for mm_meridian, mm_sportrak, magellan_icon_mapping_t, mm_gps315320, mm_unknown, mm_map330, mm_map410, pid_to_model_t, mm_gps310, m330_cleanse, mag_checksum, mag_find_descr_from_token, mag_find_token_from_descr, mag_rteparse, mag_trkparse
+#include "src/core/datetime.h" // for DateTime
+#include "vecs.h" // for Vecs
+
+
+static int bitrate = 4800;
+static int wptcmtcnt;
+static int wptcmtcnt_max;
+static int explorist;
+static int broken_sportrak;
+#define MYNAME "MAGPROTO"
+#define MAXCMTCT 200
+
+#define debug_serial (global_opts.debug_level > 1)
+
+static QString termread(char* ibuf, int size);
+static void termwrite(const char* obuf, int size);
+static void mag_readmsg(gpsdata_type objective);
+static void mag_handon();
+static void mag_handoff();
+static short_handle mkshort_handle = nullptr;
+static char* deficon = nullptr;
+static char* bs = nullptr;
+static char* cmts = nullptr;
+static char* noack = nullptr;
+static char* nukewpt = nullptr;
+static int route_out_count;
+static int waypoint_read_count;
+static int wpt_len = 8;
+static QString curfname;
+static int extension_hint;
+// For Explorist GC/510/610/710 families, bludgeon in GPX support.
+// (This has nothing to do with the Explorist 100...600 products.)
+static Format* gpx_vec;
+static mag_info* explorist_info;
+static QStringList os_gpx_files(const char* dirname);
+
+/*
+ * Magellan's firmware is *horribly* slow to send the next packet after
+ * we turn around an ack while we are reading from the device. It's
+ * quite spiffy when we're writing to the device. Since we're *way*
+ * less likely to lose data while reading from it than it is to lose data
+ * when we write to it, we turn off the acks when we are predominantly
+ * reading.
+ */
+static int suppress_ack;
+
+enum mag_rxstate {
+ mrs_handoff = 0,
+ mrs_handon,
+ mrs_awaiting_ack
+};
+
+/*
+ * An individual element of a route.
+ */
+struct mag_rte_elem {
+ QString wpt_name;
+ QString wpt_icon;
+};
+
+/*
+ * A header of a route. Related elements of a route belong to this.
+ */
+struct mag_rte_head_t {
+ QList<mag_rte_elem*> elem_list; /* list of child rte_elems */
+ char* rte_name{nullptr};
+ int nelems{0};
+};
+
+static QList<Waypoint*> rte_wpt_tmp; /* temporary PGMNWPL msgs for routes */
+
+static gbfile* magfile_h;
+static mag_rxstate magrxstate;
+static int mag_error;
+static unsigned int last_rx_csum;
+static int found_done;
+static int got_version;
+static int is_file = 0;
+static route_head* trk_head;
+static int ignore_unable;
+
+static Waypoint* mag_wptparse(char*);
+using cleanse_fn = QString (const char*);
+static cleanse_fn* mag_cleanse;
+static const char** os_get_magellan_mountpoints();
+
+static const magellan_icon_mapping_t gps315_icon_table[] = {
+ { "a", "filled circle" },
+ { "b", "box" },
+ { "c", "red buoy" },
+ { "d", "green buoy" },
+ { "e", "buoy" },
+ { "f", "rocks" },
+ { "g", "red daymark" },
+ { "h", "green daymark" },
+ { "i", "bell" },
+ { "j", "danger" },
+ { "k", "diver down" },
+ { "l", "fish" },
+ { "m", "house" },
+ { "n", "mark" },
+ { "o", "car" },
+ { "p", "tent" },
+ { "q", "boat" },
+ { "r", "food" },
+ { "s", "fuel" },
+ { "t", "tree" },
+ { nullptr, nullptr }
+};
+
+static const magellan_icon_mapping_t map330_icon_table[] = {
+ { "a", "crossed square" },
+ { "b", "box" },
+ { "c", "house" },
+ { "d", "aerial" },
+ { "e", "airport" },
+ { "f", "amusement park" },
+ { "g", "ATM" },
+ { "g", "Bank" },
+ { "h", "auto repair" },
+ { "i", "boating" },
+ { "j", "camping" },
+ { "k", "exit ramp" },
+ { "l", "first aid" },
+ { "m", "nav aid" },
+ { "n", "buoy" },
+ { "o", "fuel" },
+ { "p", "garden" },
+ { "q", "golf" },
+ { "r", "hotel" },
+ { "s", "hunting/fishing" },
+ { "t", "large city" },
+ { "u", "lighthouse" },
+ { "v", "major city" },
+ { "w", "marina" },
+ { "x", "medium city" },
+ { "y", "museum" },
+ { "z", "obstruction" },
+ { "aa", "park" },
+ { "ab", "resort" },
+ { "ac", "restaurant" },
+ { "ad", "rock" },
+ { "ae", "scuba" },
+ { "af", "RV service" },
+ { "ag", "shooting" },
+ { "ah", "sight seeing" },
+ { "ai", "small city" },
+ { "aj", "sounding" },
+ { "ak", "sports arena" },
+ { "al", "tourist info" },
+ { "am", "truck service" },
+ { "an", "winery" },
+ { "ao", "wreck" },
+ { "ap", "zoo" },
+ { "ah", "Virtual cache"}, /* Binos: because you "see" them. */
+ { "ak", "Micro-Cache" }, /* Looks like a film canister. */
+ { "an", "Multi-Cache"}, /* Winery: grapes 'coz they "bunch" */
+ { "s", "Unknown Cache"}, /* 'Surprise' cache: use a target. */
+ { "ac", "Event Cache"}, /* Event caches. May be food. */
+ { nullptr, nullptr }
+};
+
+pid_to_model_t pid_to_model[] = {
+ { mm_gps315320, 19, "ColorTrak" },
+ { mm_gps315320, 24, "GPS 315/320" },
+ { mm_map410, 25, "Map 410" },
+ { mm_map330, 30, "Map 330" },
+ { mm_gps310, 31, "GPS 310" },
+ { mm_meridian, 33, "Meridian" },
+ { mm_meridian, 35, "ProMark 2" },
+ { mm_sportrak, 36, "SporTrak Map/Pro" },
+ { mm_sportrak, 37, "SporTrak" },
+ { mm_meridian, 38, "FX324 Plotter" },
+ { mm_meridian, 39, "Meridian Color" },
+ { mm_meridian, 40, "FX324C Plotter" },
+ { mm_sportrak, 41, "Sportrak Color" },
+ { mm_sportrak, 42, "Sportrak Marine" },
+ { mm_meridian, 43, "Meridian Marine" },
+ { mm_sportrak, 44, "Sportrak Topo" },
+ { mm_sportrak, 45, "Mystic" },
+ { mm_meridian, 46, "MobileMapper" },
+ { mm_meridian, 110, "Explorist 100" },
+ { mm_meridian, 111, "Explorist 200" },
+ { mm_unknown, 0, nullptr }
+};
+
+static const magellan_icon_mapping_t* icon_mapping = map330_icon_table;
+
+/*
+ * For each receiver type, return a "cleansed" version of the string
+ * that's valid for a waypoint name or comment. The string should be
+ * freed when you're done with it.
+ */
+static QString
+m315_cleanse(const char* istring)
+{
+ char* rstring = (char*) xmalloc(strlen(istring)+1);
+ char* o;
+ const char* i;
+ static char m315_valid_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789";
+ for (o=rstring,i=istring; *i; i++) {
+ if (strchr(m315_valid_chars, toupper(*i))) {
+ *o++ = toupper(*i);
+ }
+ }
+ *o = 0;
+ QString rv(rstring);
+ xfree(rstring);
+ return rv;
+}
+
+/*
+ * Do same for 330, Meridian, and SportTrak.
+ */
+QString
+m330_cleanse(const char* istring)
+{
+ static char m330_valid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ "
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+-.'/!@#<%^&>()=:\\";
+ char* rstring = (char*) xmalloc(strlen(istring)+1);
+ char* o;
+ const char* i;
+
+ for (o=rstring,i=istring; *i; i++) {
+ if (strchr(m330_valid_chars, *i)) {
+ *o++ = *i;
+ }
+ }
+ *o = 0;
+ QString rv(rstring);
+ xfree(rstring);
+ return rv;
+}
+
+/*
+ * Given a protocol message, compute the checksum as needed by
+ * the Magellan protocol.
+ */
+unsigned int
+mag_checksum(const char* const buf)
+{
+ int csum = 0;
+
+ for (const char* p = buf; *p; p++) {
+ csum ^= *p;
+ }
+
+ return csum;
+}
+static unsigned int
+mag_pchecksum(const char* const buf, int len)
+{
+ int csum = 0;
+ const char* p = buf;
+ for (; len ; len--) {
+ csum ^= *p++;
+ }
+ return csum;
+}
+
+static void
+mag_writemsg(const char* const buf)
+{
+ unsigned int osum = mag_checksum(buf);
+ int retry_cnt = 5;
+ QScopedPointer<char, QScopedPointerPodDeleter> obuf;
+
+ if (debug_serial) {
+ warning("WRITE: $%s*%02X\r\n",buf, osum);
+ }
+
+retry:
+
+ int i = xasprintf(obuf, "$%s*%02X\r\n",buf, osum);
+ termwrite(obuf.data(), i);
+ if (magrxstate == mrs_handon || magrxstate == mrs_awaiting_ack) {
+ magrxstate = mrs_awaiting_ack;
+ mag_readmsg(trkdata);
+ if (last_rx_csum != osum) {
+ if (debug_serial) {
+ warning("COMM ERROR: Expected %02x, got %02x",
+ osum, last_rx_csum);
+ }
+ if (retry_cnt--) {
+ goto retry;
+ } else {
+ mag_handoff();
+ fatal(MYNAME
+ ": Too many communication errors.\n");
+ }
+ }
+ }
+}
+
+static void
+mag_writeack(int osum)
+{
+ QScopedPointer<char, QScopedPointerPodDeleter> nbuf;
+ QScopedPointer<char, QScopedPointerPodDeleter> obuf;
+
+ if (is_file) {
+ return;
+ }
+
+ (void) xasprintf(nbuf, "PMGNCSM,%02X", osum);
+ unsigned int nsum = mag_checksum(nbuf.data());
+ int i = xasprintf(obuf, "$%s*%02X\r\n",nbuf.data(), nsum);
+
+ if (debug_serial) {
+ warning("ACK WRITE: %s",obuf.data());
+ }
+ /*
+ * Don't call mag_writemsg here so we don't get into ack feedback
+ * loops.
+ */
+ termwrite(obuf.data(), i);
+}
+
+static void
+mag_handon()
+{
+ if (!is_file) {
+ mag_writemsg("PMGNCMD,HANDON");
+ }
+ magrxstate = mrs_handon;
+
+}
+
+static void
+mag_handoff()
+{
+ if (!is_file) {
+ mag_writemsg("PMGNCMD,HANDOFF");
+ }
+ magrxstate = mrs_handoff;
+}
+
+static void
+mag_verparse(char* ibuf)
+{
+ int prodid = mm_unknown;
+ char version[1024];
+ pid_to_model_t* pp = pid_to_model;
+
+ got_version = 1;
+ sscanf(ibuf,"$PMGNVER,%d,%[^,]", &prodid, version);
+
+ for (pp = pid_to_model; pp->model != mm_unknown; pp++) {
+ if (pp->pid == prodid) {
+ break;
+ }
+ }
+
+ if (prodid == 37) {
+ broken_sportrak = 1;
+ }
+
+ switch (pp->model) {
+ case mm_gps315320:
+ case mm_map410:
+ icon_mapping = gps315_icon_table;
+ setshort_length(mkshort_handle, 6);
+ setshort_mustupper(mkshort_handle, 1);
+ mag_cleanse = m315_cleanse;
+ break;
+ case mm_map330:
+ case mm_meridian:
+ case mm_sportrak:
+ icon_mapping = map330_icon_table;
+ setshort_length(mkshort_handle, wpt_len);
+ setshort_mustupper(mkshort_handle, 0);
+ mag_cleanse = m330_cleanse;
+ break;
+ default:
+ fatal(MYNAME ": Unknown receiver type %d, model version '%s'.\n", prodid, version);
+ }
+}
+
+#define IS_TKN(x) (strncmp(ibuf,x, sizeof(x)-1) == 0)
+
+static void
+mag_readmsg(gpsdata_type objective)
+{
+ char ibuf[512]; /* oliskoli: corrupted data (I've seen descr with a lot
+ of escaped FFFFFFFF) may need more size */
+ int retrycnt = 20;
+
+retry:
+ QString gr = termread(ibuf, sizeof(ibuf));
+
+ if (gr.isEmpty()) {
+ if (!got_version) {
+ /*
+ * The 315 can take up to six seconds to respond to
+ * a VERSION command. Since this is on startup,
+ * we'll be fairly persistent in retrying.
+ */
+ if (retrycnt--) {
+ goto retry;
+ } else {
+ fatal(MYNAME ": No data received from GPS.\n");
+ }
+ } else {
+ if (is_file) {
+ found_done = 1;
+ }
+ return;
+ }
+ }
+
+ /* If column zero isn't a dollar sign, it's not for us */
+ if (ibuf[0] != '$') {
+ fatal(MYNAME ": line doesn't start with '$'.\n");
+ }
+
+
+ int isz = strlen(ibuf);
+
+ if (isz < 5) {
+ if (debug_serial) {
+ warning("SHORT READ %d\n", isz);
+ }
+ return;
+ }
+ mag_error = 0;
+ while (!isprint(ibuf[isz])) {
+ isz--;
+ }
+ char* isump = &ibuf[isz-1];
+ unsigned int isum = strtoul(isump, nullptr,16);
+ if (isum != mag_pchecksum(&ibuf[1], isz-3)) {
+ if (debug_serial) {
+ warning("RXERR %02x/%02x: '%s'\n", isum, mag_pchecksum(&ibuf[1],isz-5), ibuf);
+ }
+ /* Special case receive errors early on. */
+ if (!got_version) {
+ fatal(MYNAME ": bad communication. Check bit rate.\n");
+ }
+ }
+ if (debug_serial) {
+ warning("READ: %s\n", ibuf);
+ }
+ if (IS_TKN("$PMGNCSM,")) {
+ last_rx_csum = strtoul(&ibuf[9], nullptr, 16);
+ magrxstate = mrs_handon;
+ return;
+ }
+ if (strncmp(ibuf, "$PMGNWPL,", 7) == 0) {
+ Waypoint* wpt = mag_wptparse(ibuf);
+ waypoint_read_count++;
+ if (global_opts.verbose_status) {
+ waypt_status_disp(waypoint_read_count,
+ waypoint_read_count);
+ }
+
+ if (extension_hint) {
+ if (extension_hint == WPTDATAMASK) {
+ waypt_add(wpt);
+ } else if (extension_hint == RTEDATAMASK) {
+ rte_wpt_tmp.append(wpt);
+ }
+ } else {
+ switch (objective) {
+ case wptdata:
+ waypt_add(wpt);
+ break;
+ case rtedata:
+ rte_wpt_tmp.append(wpt);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (strncmp(ibuf, "$PMGNTRK,", 7) == 0) {
+ Waypoint* wpt = mag_trkparse(ibuf);
+ /*
+ * Allow lazy allocation of track head.
+ */
+ if (trk_head == nullptr) {
+ /* These tracks don't have names, so derive one
+ * from input filename.
+ */
+
+ trk_head = new route_head;
+
+ /* Whack trailing extension if present. */
+ QString s = get_filename(curfname);
+ int idx = s.indexOf('.');
+ if (idx > 0) {
+ s.truncate(idx);
+ }
+
+ trk_head->rte_name = s;
+ track_add_head(trk_head);
+ }
+
+ track_add_wpt(trk_head, wpt);
+ }
+ if (strncmp(ibuf, "$PMGNRTE,", 7) == 0) {
+ mag_rteparse(ibuf);
+ }
+ if (IS_TKN("$PMGNVER,")) {
+ mag_verparse(ibuf);
+ }
+ mag_error = 0;
+ if (!ignore_unable && IS_TKN("$PMGNCMD,UNABLE")) {
+ warning("Unable to send\n");
+ found_done = 1;
+ mag_error = 1;
+ ignore_unable = 0;
+ return;
+ }
+ if (IS_TKN("$PMGNCMD,END") || (is_file && (gbfeof(magfile_h)))) {
+ found_done = 1;
+ return;
+ }
+
+ if (magrxstate != mrs_handoff) {
+ mag_writeack(isum);
+ }
+}
+
+static void* serial_handle = nullptr;
+
+static int
+terminit(const QString& portname, int create_ok)
+{
+ if (gbser_is_serial(qPrintable(portname))) {
+ if (serial_handle = gbser_init(qPrintable(portname)), nullptr != serial_handle) {
+ int rc;
+ if (rc = gbser_set_port(serial_handle, bitrate, 8, 0, 1), gbser_OK != rc) {
+ fatal(MYNAME ": Can't configure port\n");
+ }
+ }
+ is_file = 0;
+ if (serial_handle == nullptr) {
+ fatal(MYNAME ": Could not open serial port %s\n", qPrintable(portname));
+ }
+ return 1;
+ } else {
+ /* Does this check for an error? */
+ magfile_h = gbfopen(portname, create_ok ? "w+b" : "rb", MYNAME);
+ is_file = 1;
+ icon_mapping = map330_icon_table;
+ mag_cleanse = m330_cleanse;
+ got_version = 1;
+ return 0;
+ }
+}
+
+static QString termread(char* ibuf, int size)
+{
+ if (is_file) {
+ return gbfgets(ibuf, size, magfile_h);
+ } else {
+ int rc = gbser_read_line(serial_handle, ibuf, size, 2000, 0x0a, 0x0d);
+ if (rc != gbser_OK) {
+ fatal(MYNAME ": Read error\n");
+ }
+ return ibuf;
+ }
+}
+
+/* Though not documented in the protocol spec, if the unit itself
+ * wants to create a field containing a comma, it will encode it
+ * as <escape>2C. We extrapolate that any 2 digit hex encoding may
+ * be valid. We don't do this in termread() since we need to do it
+ * after the scanf. This means we have to do it field-by-field
+ * basis.
+ *
+ * The buffer is modified in place and shortened by copying the remaining
+ * string including the terminator.
+ */
+static
+void
+mag_dequote(char* ibuf)
+{
+ char* esc = nullptr;
+
+ while ((esc = strchr(ibuf, 0x1b))) {
+ int nremains = strlen(esc);
+ if (nremains >= 3) {
+ static const char hex[17] = "0123456789ABCDEF";
+ const char* c1 = strchr(hex, esc[1]);
+ const char* c2 = strchr(hex, esc[2]);
+ if (c1 && c2) {
+ int escv = (c1 - hex) * 16 + (c2 - hex);
+ if (escv == 255) { /* corrupted data */
+ char* tmp = esc + 1;
+ while (*tmp == 'F') {
+ tmp++;
+ }
+ memmove(esc, tmp, strlen(tmp) + 1);
+ } else {
+ *esc++ = (isprint(escv)) ? escv : '$';
+ /* buffers overlap */
+ memmove(esc, esc+2, nremains - 2);
+ }
+ }
+ } else {
+ *esc = '\0'; /* trim corrupted data,
+ otherwise we get an endless loop */
+ }
+ }
+}
+
+static void
+termwrite(const char* obuf, int size)
+{
+ if (is_file) {
+ size_t nw;
+ if (nw = gbfwrite(obuf, 1, size, magfile_h), nw < (size_t) size) {
+ fatal(MYNAME ": Write error");
+ }
+ } else {
+ int rc;
+ if (rc = gbser_write(serial_handle, obuf, size), rc < 0) {
+ fatal(MYNAME ": Write error");
+ }
+ }
+}
+
+static void termdeinit()
+{
+ if (is_file) {
+ gbfclose(magfile_h);
+ magfile_h = nullptr;
+ } else {
+ gbser_deinit(serial_handle);
+ serial_handle = nullptr;
+ }
+}
+
+/*
+ * Arg tables are doubled up so that -? can output appropriate help
+ */
+static
+QVector<arglist_t> mag_sargs = {
+ {
+ "deficon", &deficon, "Default icon name", nullptr, ARGTYPE_STRING,
+ ARG_NOMINMAX, nullptr
+ },
+ {
+ "maxcmts", &cmts, "Max number of comments to write (maxcmts=200)",
+ "200", ARGTYPE_INT, ARG_NOMINMAX, nullptr
+ },
+ {
+ "baud", &bs, "Numeric value of bitrate (baud=4800)", "4800",
+ ARGTYPE_INT, ARG_NOMINMAX, nullptr
+ },
+ {
+ "noack", &noack, "Suppress use of handshaking in name of speed",
+ nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
+ },
+ {
+ "nukewpt", &nukewpt, "Delete all waypoints", nullptr, ARGTYPE_BOOL,
+ ARG_NOMINMAX, nullptr
+ },
+};
+
+static
+QVector<arglist_t> mag_fargs = {
+ {
+ "deficon", &deficon, "Default icon name", nullptr, ARGTYPE_STRING,
+ ARG_NOMINMAX, nullptr
+ },
+ {
+ "maxcmts", &cmts, "Max number of comments to write (maxcmts=200)",
+ nullptr, ARGTYPE_INT, ARG_NOMINMAX, nullptr
+ },
+};
+
+/*
+ * The part of the serial init that's common to read and write.
+ */
+static void
+mag_serial_init_common(const QString& portname)
+{
+ if (is_file) {
+ return;
+ }
+
+ mag_handoff();
+ if (!noack && !suppress_ack) {
+ mag_handon();
+ }
+
+ time_t now = current_time().toTime_t();
+ /*
+ * The 315 can take up to 4.25 seconds to respond to initialization
+ * commands. Time out on the side of caution.
+ */
+ time_t later = now + 6;
+ got_version = 0;
+ mag_writemsg("PMGNCMD,VERSION");
+
+ while (!got_version) {
+ mag_readmsg(trkdata);
+ if (current_time().toTime_t() > later) {
+ fatal(MYNAME ": No acknowledgment from GPS on %s\n",
+ qPrintable(portname));
+ }
+ }
+
+ if ((icon_mapping != gps315_icon_table)) {
+ /*
+ * The 315 can't handle this command, so we set a global
+ * to ignore the NAK on it.
+ */
+ ignore_unable = 1;
+ mag_writemsg("PMGNCMD,NMEAOFF");
+ ignore_unable = 0;
+ }
+
+ if (nukewpt) {
+ /* The unit will send us an "end" message upon completion */
+ mag_writemsg("PMGNCMD,DELETE,WAYPOINT");
+ mag_readmsg(trkdata);
+ if (!found_done) {
+ fatal(MYNAME ": Unexpected response to waypoint delete command.\n");
+ }
+ found_done = 0;
+ }
+
+}
+static void
+mag_rd_init_common(const QString& portname)
+{
+ waypoint_read_count = 0;
+ // For Explorist GC, intercept the device access and redirect to GPX.
+ // We actually do the rd_init() inside read as we may have multiple
+ // files that we have to read.
+ if (portname == "usb:") {
+ const char** dlist = os_get_magellan_mountpoints();
+ explorist_info = explorist_ini_get(dlist);
+ if (explorist_info) {
+ gpx_vec = Vecs::Instance().find_vec("gpx");
+ }
+ return;
+ }
+
+ if (bs) {
+ bitrate=xstrtoi(bs, nullptr, 10);
+ }
+
+ if (!mkshort_handle) {
+ mkshort_handle = mkshort_new_handle();
+ }
+
+ terminit(portname, 0);
+ mag_serial_init_common(portname);
+
+ rte_wpt_tmp.clear();
+
+ /* find the location of the tail of the path name,
+ * make a copy of it, then lop off the file extension
+ */
+
+ curfname = get_filename(portname);
+
+ /*
+ * I'd rather not derive behaviour from filenames but since
+ * we can't otherwise tell if we should put a WPT on the route
+ * queue or the WPT queue in the presence of (-w -r -t) we
+ * divine a hint from the filename extension when we can.
+ */
+ QString exten = QFileInfo(curfname).suffix();
+ if (exten.length() > 0) {
+ if (0 == exten.compare(QLatin1String("upt"), Qt::CaseInsensitive)) {
+ extension_hint = WPTDATAMASK;
+ } else if (0 == exten.compare(QLatin1String("log"), Qt::CaseInsensitive)) {
+ extension_hint = TRKDATAMASK;
+ } else if (0 == exten.compare(QLatin1String("rte"), Qt::CaseInsensitive)) {
+ extension_hint = RTEDATAMASK;
+ }
+ }
+
+}
+
+static void
+mag_rd_init(const QString& portname)
+{
+ explorist = 0;
+ suppress_ack = 1;
+ mag_rd_init_common(portname);
+}
+
+static void
+magX_rd_init(const QString& portname)
+{
+ explorist = 1;
+ mag_rd_init_common(portname);
+}
+
+static void
+mag_wr_init_common(const QString& portname)
+{
+ suppress_ack = 0;
+ if (bs) {
+ bitrate=xstrtoi(bs, nullptr, 10);
+ }
+
+ if (waypt_count() > 500) {
+ fatal(MYNAME ": Meridian/Explorist does not support more than 500 waypoints in one file. Only\n200 waypoints may have comments.\nDecrease the number of waypoints sent.\n");
+ }
+
+ if (cmts) {
+ wptcmtcnt_max = xstrtoi(cmts, nullptr, 10);
+ } else {
+ wptcmtcnt_max = MAXCMTCT ;
+ }
+
+ if (!mkshort_handle) {
+ mkshort_handle = mkshort_new_handle();
+ }
+
+ terminit(portname, 1);
+ mag_serial_init_common(portname);
+
+ rte_wpt_tmp.clear();
+}
+
+/*
+ * Entry point for extended (explorist) points.
+ */
+static void
+magX_wr_init(const QString& portname)
+{
+ wpt_len = 20;
+ explorist = 1;
+ mag_wr_init_common(portname);
+ setshort_length(mkshort_handle, wpt_len);
+ setshort_whitespace_ok(mkshort_handle, 1);
+}
+
+static void
+mag_wr_init(const QString& portname)
+{
+ explorist = 0;
+ wpt_len = 8;
+ mag_wr_init_common(portname);
+ /*
+ * Whitespace is actually legal, but since waypoint name length is
+ * only 8 bytes, we'll conserve them.
+ */
+
+ setshort_whitespace_ok(mkshort_handle, 0);
+}
+
+static void
+mag_deinit()
+{
+ if (explorist_info) {
+ explorist_ini_done(explorist_info);
+ return;
+ }
+ mag_handoff();
+ termdeinit();
+ if (mkshort_handle) {
+ mkshort_del_handle(&mkshort_handle);
+ }
+
+ while (!rte_wpt_tmp.isEmpty()) {
+ delete rte_wpt_tmp.takeFirst();
+ }
+
+ trk_head = nullptr;
+
+ curfname.clear();
+}
+
+static void
+mag_wr_deinit()
+{
+ if (explorist) {
+ mag_writemsg("PMGNCMD,END");
+ }
+ mag_deinit();
+}
+
+/*
+ * I'm tired of arguing with scanf about optional fields . Detokenize
+ * an incoming string that may contain empty fields.
+ *
+ * Probably should be cleaned up and moved to common code, but
+ * making it deal with an arbitrary number of fields of arbitrary
+ * size is icky. We don't have to solve the general case here...
+ */
+
+static char ifield[20][100];
+static
+void parse_istring(char* istring)
+{
+ int f = 0;
+ int n;
+ while (istring[0]) {
+ char* fp = ifield[f];
+ int x = sscanf(istring, "%[^,]%n", fp, &n);
+ f++;
+ if (x) {
+ istring += n;
+ /* IF more in this string, skip delim */
+ if (istring[0]) {
+ istring++;
+ }
+ } else {
+ istring ++;
+ }
+ }
+}
+
+/*
+ * Given an incoming track messages of the form:
+ * $PMGNTRK,3605.259,N,08644.389,W,00151,M,201444.61,A,,020302*66
+ * create and return a populated waypoint.
+ */
+Waypoint*
+mag_trkparse(char* trkmsg)
+{
+ int hms;
+ int fracsecs;
+ struct tm tm;
+
+ auto* waypt = new Waypoint;
+
+ memset(&tm, 0, sizeof(tm));
+
+ /*
+ * As some of the fields are optional, sscanf works badly
+ * for us.
+ */
+ parse_istring(trkmsg);
+ double latdeg = strtod(ifield[1], nullptr);
+ char latdir = ifield[2][0];
+ double lngdeg = strtod(ifield[3], nullptr);
+ char lngdir = ifield[4][0];
+ int alt = strtod(ifield[5], nullptr);
+ char altunits = ifield[6][0];
+ (void)altunits;
+ sscanf(ifield[7], "%d.%d", &hms, &fracsecs);
+ /* Field 8 is constant */
+ /* Field nine is optional track name */
+ int dmy = xstrtoi(ifield[10], nullptr, 10);
+ int sec = hms % 100;
+ hms = hms / 100;
+ int min = hms % 100;
+ hms = hms / 100;
+ int hour = hms % 100;
+
+ int year = 100 + dmy % 100 + 1900;
+ dmy = dmy / 100;
+ int mon = dmy % 100;
+ dmy = dmy / 100;
+ int day = dmy % 100;
+ QDateTime dt(QDate(year, mon, day), QTime(hour, min, sec, fracsecs * 10), Qt::UTC);
+ waypt->SetCreationTime(dt);
+
+ if (latdir == 'S') {
+ latdeg = -latdeg;
+ }
+ waypt->latitude = ddmm2degrees(latdeg);
+
+ if (lngdir == 'W') {
+ lngdeg = -lngdeg;
+ }
+ waypt->longitude = ddmm2degrees(lngdeg);
+
+ waypt->altitude = alt;
+
+ return waypt;
+
+}
+
+/*
+ * Given an incoming route messages of the form:
+ * $PMGNRTE,4,1,c,1,DAD,a,Anna,a*61
+ * generate a route.
+ */
+void
+mag_rteparse(char* rtemsg)
+{
+ int n;
+ int frags,frag,rtenum;
+ char xbuf[100],next_stop[100],abuf[100];
+ char* currtemsg;
+ static mag_rte_head_t* mag_rte_head;
+
+#if 0
+ sscanf(rtemsg,"$PMGNRTE,%d,%d,%c,%d%n",
+ &frags,&frag,xbuf,&rtenum,&n);
+#else
+ sscanf(rtemsg,"$PMGNRTE,%d,%d,%c,%d%n",
+ &frags,&frag,xbuf,&rtenum,&n);
+
+ /* Explorist has a route name here */
+ QString rte_name;
+ if (explorist) {
+ char* ca = rtemsg + n;
+ if (*ca++ != ',') {
+ fatal(MYNAME ": Incorrectly formatted route line '%s'", rtemsg);
+ }
+
+ char* ce = strchr(ca, ',');
+ if (ce == nullptr) {
+ fatal(MYNAME ": Incorrectly formatted route line '%s'", rtemsg);
+ }
+
+ if (ca == ce) {
+ rte_name = "Route";
+ rte_name += QString::number(rtenum);
+ } else {
+ rte_name = ca;
+ rte_name.truncate(ce-ca);
+ }
+
+ n += ((ce - ca) + 1);
+ }
+
+#endif
+
+ /*
+ * This is the first component of a route. Allocate a new
+ * head.
+ */
+ if (frag == 1) {
+ mag_rte_head = new mag_rte_head_t;
+ mag_rte_head->nelems = frags;
+ }
+
+ currtemsg = rtemsg + n;
+
+ /*
+ * The individual line may contain several route elements.
+ * loop and pick those up.
+ */
+ while (sscanf(currtemsg,",%[^,],%[^,]%n",next_stop, abuf,&n)) {
+ if ((next_stop[0] == 0) || (next_stop[0] == '*')) {
+ break;
+ }
+
+ /* trim CRC from waypoint icon string */
+ if (char* p = strchr(abuf, '*'); p != nullptr) {
+ *p = '\0';
+ }
+
+ auto* rte_elem = new mag_rte_elem;
+
+ rte_elem->wpt_name = next_stop;
+ rte_elem->wpt_icon = abuf;
+
+ mag_rte_head->elem_list.append(rte_elem);
+
+ /* Sportrak (the non-mapping unit) creates malformed
+ * RTE sentence with no icon info after the routepoint
+ * name. So if we saw an "icon" treat that as new
+ * routepoint.
+ */
+ if (broken_sportrak && abuf[0]) {
+ rte_elem = new mag_rte_elem;
+ rte_elem->wpt_name = abuf;
+
+ mag_rte_head->elem_list.append(rte_elem);
+ }
+
+ next_stop[0] = 0;
+ currtemsg += n;
+ }
+
+ /*
+ * If this was the last fragment of the route, add it to the
+ * gpsbabel internal structs now.
+ */
+ if (frag == mag_rte_head->nelems) {
+
+ auto* rte_head = new route_head;
+ route_add_head(rte_head);
+ rte_head->rte_num = rtenum;
+ rte_head->rte_name = rte_name;
+
+ /*
+ * It is quite feasible that we have 200 waypoints,
+ * 3 of which are used in the route. We'll need to find
+ * those in the queue for SD routes...
+ */
+
+ while (!mag_rte_head->elem_list.isEmpty()) {
+ mag_rte_elem* re = mag_rte_head->elem_list.takeFirst();
+
+ /*
+ * Copy route points from temp wpt queue.
+ */
+ foreach (const Waypoint* waypt, rte_wpt_tmp) {
+ if (waypt->shortname == re->wpt_name) {
+ auto* wpt = new Waypoint(*waypt);
+ route_add_wpt(rte_head, wpt);
+ break;
+ }
+ }
+
+ delete re;
+ }
+ delete mag_rte_head;
+ }
+}
+
+QString
+mag_find_descr_from_token(const char* token)
+{
+ if (icon_mapping == nullptr) {
+ return "unknown";
+ }
+
+ for (const magellan_icon_mapping_t* i = icon_mapping; i->token; i++) {
+ if (token[0] == 0) {
+ break;
+ }
+ if (case_ignore_strcmp(token, i->token) == 0) {
+ return i->icon;
+ }
+ }
+ return icon_mapping[0].icon;
+}
+
+QString
+mag_find_token_from_descr(const QString& icon)
+{
+ const magellan_icon_mapping_t* i = icon_mapping;
+
+ if (i == nullptr || icon == nullptr) {
+ return "a";
+ }
+
+ for (i = icon_mapping; i->token; i++) {
+ if (icon.compare(i->icon, Qt::CaseInsensitive) == 0) {
+ return i->token;
+ }
+ }
+ return icon_mapping[0].token;
+}
+
+/*
+ * Given an incoming waypoint messages of the form:
+ * $PMGNWPL,3549.499,N,08650.827,W,0000257,M,HOME,HOME,c*4D
+ * create and return a populated waypoint.
+ */
+static Waypoint*
+mag_wptparse(char* trkmsg)
+{
+ double latdeg, lngdeg;
+ char latdir;
+ char lngdir;
+ int alt;
+ char altunits;
+ char shortname[100];
+ char descr[256];
+ char icon_token[100];
+ int i = 0;
+
+ descr[0] = 0;
+ icon_token[0] = 0;
+
+ auto* waypt = new Waypoint;
+
+ sscanf(trkmsg,"$PMGNWPL,%lf,%c,%lf,%c,%d,%c,%[^,],%[^,]",
+ &latdeg,&latdir,
+ &lngdeg,&lngdir,
+ &alt,&altunits,shortname,descr);
+ char* icone = strrchr(trkmsg, '*');
+ char* icons = strrchr(trkmsg, ',')+1;
+
+ mag_dequote(descr);
+
+ for (char* blah = icons ; blah < icone; blah++) {
+ icon_token[i++] = *blah;
+ }
+ icon_token[i++] = '\0';
+
+ if (latdir == 'S') {
+ latdeg = -latdeg;
+ }
+ waypt->latitude = ddmm2degrees(latdeg);
+
+ if (lngdir == 'W') {
+ lngdeg = -lngdeg;
+ }
+ waypt->longitude = ddmm2degrees(lngdeg);
+
+ waypt->altitude = alt;
+ waypt->shortname = shortname;
+ waypt->description = descr;
+ waypt->icon_descr = mag_find_descr_from_token(icon_token);
+
+ return waypt;
+}
+
+static void
+mag_read()
+{
+ if (gpx_vec) {
+ QStringList f = os_gpx_files(explorist_info->track_path);
+ for (const auto& file : qAsConst(f)) {
+ gpx_vec->rd_init(file);
+ gpx_vec->read();
+ gpx_vec->rd_deinit();
+ }
+
+ f = os_gpx_files(explorist_info->waypoint_path);
+ for (const auto& file : qAsConst(f)) {
+ gpx_vec->rd_init(file);
+ gpx_vec->read();
+ gpx_vec->rd_deinit();
+ }
+#if 0
+ f = os_gpx_files(explorist_info->geo_path);
+ for (const auto& file : qAsConst(f)) {
+ gpx_vec->rd_init(file);
+ gpx_vec->read();
+ gpx_vec->rd_deinit();
+ }
+#endif
+ return;
+ }
+
+ found_done = 0;
+ if (global_opts.masked_objective & TRKDATAMASK) {
+ magrxstate = mrs_handoff;
+ if (!is_file) {
+ mag_writemsg("PMGNCMD,TRACK,2");
+ }
+
+ while (!found_done) {
+ mag_readmsg(trkdata);
+ }
+ }
+
+ found_done = 0;
+ if (global_opts.masked_objective & WPTDATAMASK) {
+ magrxstate = mrs_handoff;
+ if (!is_file) {
+ mag_writemsg("PMGNCMD,WAYPOINT");
+ }
+
+ while (!found_done) {
+ mag_readmsg(wptdata);
+ }
+ }
+
+ found_done = 0;
+ if (global_opts.masked_objective & RTEDATAMASK) {
+ magrxstate = mrs_handoff;
+ if (!is_file) {
+ /*
+ * serial routes require waypoint & routes
+ * messages commands.
+ */
+ mag_writemsg("PMGNCMD,WAYPOINT");
+
+ while (!found_done) {
+ mag_readmsg(rtedata);
+ }
+
+ mag_writemsg("PMGNCMD,ROUTE");
+
+ found_done = 0;
+ while (!found_done) {
+ mag_readmsg(rtedata);
+ }
+ } else {
+ /*
+ * SD routes are a stream of PMGNWPL and
+ * PMGNRTE messages, in that order.
+ */
+ while (!found_done) {
+ mag_readmsg(rtedata);
+ }
+ }
+ }
+}
+
+static
+void
+mag_waypt_pr(const Waypoint* waypointp)
+{
+ QScopedPointer<char, QScopedPointerPodDeleter> obuf;
+ QScopedPointer<char, QScopedPointerPodDeleter> ofmtdesc;
+ QString icon_token;
+
+ double ilat = waypointp->latitude;
+ double ilon = waypointp->longitude;
+
+ double lon = fabs(ilon);
+ double lat = fabs(ilat);
+
+ int lon_deg = lon;
+ int lat_deg = lat;
+
+ lon = (lon - lon_deg) * 60.0;
+ lat = (lat - lat_deg) * 60.0;
+
+ lon = (lon_deg * 100.0 + lon);
+ lat = (lat_deg * 100.0 + lat);
+
+ if (deficon) {
+ icon_token = mag_find_token_from_descr(deficon);
+ } else {
+ icon_token = mag_find_token_from_descr(waypointp->icon_descr);
+ }
+
+ if (!get_cache_icon(waypointp).isEmpty()) {
+ icon_token = mag_find_token_from_descr(get_cache_icon(waypointp));
+ }
+
+ QString isrc = waypointp->notes.isEmpty() ? waypointp->description : waypointp->notes;
+ QString owpt = global_opts.synthesize_shortnames ?
+ mkshort_from_wpt(mkshort_handle, waypointp) : waypointp->shortname;
+ QString odesc = isrc;
+ owpt = mag_cleanse(CSTRc(owpt));
+
+ if (global_opts.smart_icons &&
+ waypointp->gc_data->diff && waypointp->gc_data->terr) {
+ // It's a string and compactness counts, so "1.0" is OK to be "10".
+ xasprintf(ofmtdesc, "%ud/%ud %s", waypointp->gc_data->diff,
+ waypointp->gc_data->terr, CSTRc(odesc));
+ odesc = mag_cleanse(ofmtdesc.data());
+ } else {
+ odesc = mag_cleanse(CSTRc(odesc));
+ }
+
+ /*
+ * For the benefit of DirectRoute (which uses waypoint comments
+ * to deliver turn-by-turn popups for street routing) allow a
+ * cap on the comments delivered so we leave space for it to route.
+ */
+ if (!odesc.isEmpty() && (wptcmtcnt++ >= wptcmtcnt_max)) {
+ odesc.clear();
+ }
+
+ xasprintf(obuf, "PMGNWPL,%4.3f,%c,%09.3f,%c,%07.0f,M,%-.*s,%-.46s,%s",
+ lat, ilat < 0 ? 'S' : 'N',
+ lon, ilon < 0 ? 'W' : 'E',
+ waypointp->altitude == unknown_alt ?
+ 0 : waypointp->altitude,
+ wpt_len,
+ CSTRc(owpt),
+ CSTRc(odesc),
+ CSTR(icon_token));
+ mag_writemsg(obuf.data());
+
+ if (!is_file) {
+ if (mag_error) {
+ warning("Protocol error Writing '%s'\n", obuf.data());
+ }
+ }
+}
+
+static
+void mag_track_disp(const Waypoint* waypointp)
+{
+ QScopedPointer<char, QScopedPointerPodDeleter> obuf;
+
+ double ilat = waypointp->latitude;
+ double ilon = waypointp->longitude;
+
+ QByteArray dmy("");
+ QByteArray hms("");
+ if (waypointp->creation_time.isValid()) {
+ // Round to hundredths of seconds before conversion to string.
+ // Rounding can ripple all the way from the msec to the year.
+ QDateTime dt = waypointp->GetCreationTime().toUTC();
+ dt = dt.addMSecs(10 * lround(dt.time().msec()/10.0) - dt.time().msec());
+ assert((dt.time().msec() % 10) == 0);
+ dmy = dt.toString(u"ddMMyy").toUtf8();
+ hms = dt.toString(u"hhmmss.zzz").left(9).toUtf8();
+ }
+
+ double lon = fabs(ilon);
+ double lat = fabs(ilat);
+
+ int lon_deg = lon;
+ int lat_deg = lat;
+
+ lon = (lon - lon_deg) * 60.0;
+ lat = (lat - lat_deg) * 60.0;
+
+ lon = (lon_deg * 100.0 + lon);
+ lat = (lat_deg * 100.0 + lat);
+
+ xasprintf(obuf,"PMGNTRK,%4.3f,%c,%09.3f,%c,%05.0f,%c,%s,A,,%s",
+ lat, ilat < 0 ? 'S' : 'N',
+ lon, ilon < 0 ? 'W' : 'E',
+ waypointp->altitude == unknown_alt ?
+ 0 : waypointp->altitude,
+ 'M', hms.constData(), dmy.constData());
+ mag_writemsg(obuf.data());
+}
+
+static
+void mag_track_pr()
+{
+ track_disp_all(nullptr, nullptr, mag_track_disp);
+}
+
+/*
+The spec says to stack points:
+ $PMGNRTE,2,1,c,1,FOO,POINT1,b,POINT2,c,POINT3,d*6C<CR><LF>
+
+Meridian SD card and serial (at least) writes in pairs:
+ $PMGNRTE,4,1,c,1,HOME,c,I49X73,a*15
+ ...
+ $PMGNRTE,4,4,c,1,RON273,a,MYCF93,a*7B
+
+The spec also says that some units don't like single-legged pairs,
+and to replace the 2nd name with "<<>>", but I haven't seen one of those.
+*/
+
+static void
+mag_route_trl(const route_head* rte)
+{
+ QScopedPointer<char, QScopedPointerPodDeleter> obuff;
+ QString buff1;
+ QString buff2;
+ QString icon_token;
+
+ /* count waypoints for this route */
+ int i = rte->rte_waypt_ct();
+
+ /* number of output PMGNRTE messages at 2 points per line */
+ int numlines = (i / 2) + (i % 2);
+
+ /* increment the route counter. */
+ route_out_count++;
+
+ int thisline = i = 0;
+ foreach (const Waypoint* waypointp, rte->waypoint_list) {
+ i++;
+
+ if (deficon) {
+ icon_token = mag_find_token_from_descr(deficon);
+ } else {
+ icon_token = mag_find_token_from_descr(waypointp->icon_descr);
+ }
+
+ QString* pbuff;
+ if (i == 1) {
+ pbuff = &buff1;
+ } else {
+ pbuff = &buff2;
+ }
+ // Write name, icon tuple into alternating buff1/buff2 buffer.
+ *pbuff = waypointp->shortname + ',' + icon_token;
+
+ if ((waypointp == rte->waypoint_list.back()) || ((i % 2) == 0)) {
+ QString expbuf;
+ thisline++;
+ if (explorist) {
+ expbuf = rte->rte_name + ',';
+ }
+
+ xasprintf(obuff, "PMGNRTE,%d,%d,c,%d,%s%s,%s",
+ numlines, thisline,
+ rte->rte_num ? rte->rte_num : route_out_count,
+ CSTRc(expbuf),
+ CSTR(buff1), CSTR(buff2));
+
+ mag_writemsg(obuff.data());
+ buff1.clear();
+ buff2.clear();
+ i = 0;
+ }
+ }
+}
+
+static void
+mag_route_pr()
+{
+ route_out_count = 0;
+ route_disp_all(nullptr, mag_route_trl, mag_waypt_pr);
+
+}
+
+static void
+mag_write()
+{
+
+ wptcmtcnt = 0;
+
+ switch (global_opts.objective) {
+ case trkdata:
+ mag_track_pr();
+ break;
+ case wptdata:
+ waypt_disp_all(mag_waypt_pr);
+ break;
+ case rtedata:
+ mag_route_pr();
+ break;
+ default:
+ fatal(MYNAME ": Unknown objective.\n");
+ }
+}
+
+const char** os_get_magellan_mountpoints()
+{
+#if __APPLE__
+ const char** dlist = (const char**) xcalloc(2, sizeof *dlist);
+ dlist[0] = xstrdup("/Volumes/Magellan");
+ dlist[1] = nullptr;
+ return dlist;
+#else
+ fatal("Not implemented");
+ return nullptr;
+#endif
+}
+
+static QStringList
+os_gpx_files(const char* dirname)
+{
+ QDir dir(dirname);
+
+ const QFileInfoList filist = dir.entryInfoList(QStringList("*.gpx"), QDir::Files | QDir::Readable, QDir::Name);
+ QStringList rv;
+ for (const auto& fi : filist) {
+ rv.append(fi.absoluteFilePath());
+ }
+ return rv;
+}
+
+/*
+ * This is repeated just so it shows up as separate menu options
+ * for the benefit of GUI wrappers.
+ */
+ff_vecs_t mag_svecs = {
+ ff_type_serial,
+ FF_CAP_RW_ALL,
+ mag_rd_init,
+ mag_wr_init,
+ mag_deinit,
+ mag_deinit,
+ mag_read,
+ mag_write,
+ nullptr,
+ &mag_sargs,
+ CET_CHARSET_ASCII, 0, /* CET-REVIEW */
+ NULL_POS_OPS
+};
+
+ff_vecs_t mag_fvecs = {
+ ff_type_file,
+ FF_CAP_RW_ALL,
+ mag_rd_init,
+ mag_wr_init,
+ mag_deinit,
+ mag_deinit,
+ mag_read,
+ mag_write,
+ nullptr,
+ &mag_fargs,
+ CET_CHARSET_ASCII, 0, /* CET-REVIEW */
+ NULL_POS_OPS
+};
+
+/*
+ * Extended (Explorist) entry tables.
+ */
+ff_vecs_t magX_fvecs = {
+ ff_type_file,
+ FF_CAP_RW_ALL,
+ magX_rd_init,
+ magX_wr_init,
+ mag_deinit,
+ mag_wr_deinit,
+ mag_read,
+ mag_write,
+ nullptr,
+ &mag_fargs,
+ CET_CHARSET_ASCII, 0, /* CET-REVIEW */
+ NULL_POS_OPS
+};
+++ /dev/null
-#include "defs.h"
-#include "explorist_ini.h"
-#include "inifile.h"
-
-static inifile_t* inifile;
-static const char myname[] = "explorist";
-
-#ifdef DEAD_CODE_IS_REBORN
-static const char*
-explorist_read_value(const char* section, const char* key)
-{
- return inifile_readstr(inifile, section, key);
-}
-#endif
-
-static mag_info*
-explorist_ini_try(const char* path)
-{
- char* inipath;
-
- xasprintf(&inipath, "%s/%s", path, "APP/Atlas.ini");
- inifile = inifile_init(QString::fromUtf8(inipath), myname);
- if (!inifile) {
- xfree(inipath);
- return nullptr;
- }
-
- auto* info = (mag_info*) xmalloc(sizeof(mag_info));
- info->geo_path = nullptr;
- info->track_path = nullptr;
- info->waypoint_path = nullptr;
-
- QString s = inifile_readstr(inifile, "UGDS", "WpFolder");
- if (!s.isNull()) {
- s.replace('\\', '/');
- xasprintf(&info->waypoint_path, "%s/%s", path, CSTR(s));
- }
- s = inifile_readstr(inifile, "UGDS", "GcFolder");
- if (!s.isNull()) {
- s.replace('\\', '/');
- xasprintf(&info->geo_path, "%s/%s", path, CSTR(s));
- }
- s = inifile_readstr(inifile, "UGDS", "TrkFolder");
- if (!s.isNull()) {
- s.replace('\\', '/');
- xasprintf(&info->track_path, "%s/%s", path, CSTR(s));
- }
-
- inifile_done(inifile);
- xfree(inipath);
- return info;
-}
-
-mag_info*
-explorist_ini_get(const char** dirlist)
-{
- mag_info* r = nullptr;
- while (dirlist && *dirlist) {
- r = explorist_ini_try(*dirlist);
- if (r) {
- return r;
- }
- }
- return r;
-}
-
-void
-explorist_ini_done(mag_info* info)
-{
- xfree(info->geo_path);
- xfree(info->track_path);
- xfree(info->waypoint_path);
- xfree(info);
-}
+++ /dev/null
-
-/*
- * Interesting traits of the device from the *.ini files.
- */
-struct mag_info {
- char* geo_path;
- char* track_path;
- char* waypoint_path;
-};
-
-mag_info* explorist_ini_get(const char** directory_list);
-void explorist_ini_done(mag_info* info);
+++ /dev/null
-/*
- Copyright (C) 2002-2005 Robert Lipe, robertlipe@usa.net
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
- */
-
-#ifndef MAGELLAN_H_INCLUDED_
-#define MAGELLAN_H_INCLUDED_
-
-#include <QString>
-#include "defs.h"
-
-/*
- * Table of "interesting" Magellan models.
- * Selfishly, if I haven't heard of it, it's not in the table.
- * This doesn't mean I actually have TRIED all models listed below.
- * (Donations welcome. :-)
- */
-enum meridian_model {
- mm_unknown = 0 ,
- mm_gps315320,
- mm_map410,
- mm_map330,
- mm_gps310,
- mm_meridian,
- mm_sportrak
-};
-
-struct pid_to_model_t {
- meridian_model model;
- int pid;
- const char* model_n;
-};
-
-struct magellan_icon_mapping_t {
- const char* token;
- const char* icon;
-};
-
-QString mag_find_descr_from_token(const char* token);
-QString mag_find_token_from_descr(const QString& icon);
-
-unsigned int mag_checksum(const char*buf);
-QString m330_cleanse(const char* istring);
-
-Waypoint* mag_trkparse(char* trkmsg);
-void mag_rteparse(char* rtemsg);
-
-#endif // MAGELLAN_H_INCLUDED_
+++ /dev/null
-/*
- Communicate Thales/Magellan serial protocol.
-
- Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007,
- 2008, 2010 Robert Lipe, robertlipe+source@gpsbabel.org
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
- */
-
-#include <cassert> // for assert
-#include <cctype> // for isprint, toupper
-#include <cmath> // for fabs, lround
-#include <cstdio> // for sscanf, size_t
-#include <cstdlib> // for strtod, strtoul
-#include <cstring> // for strchr, strncmp, strlen, memmove, strrchr, memset
-
-#include <QByteArray> // for QByteArray
-#include <QDateTime> // for QDateTime
-#include <QDir> // for QDir, operator|, QDir::Files, QDir::Name, QDir::Readable
-#include <QFileInfo> // for QFileInfo
-#include <QFileInfoList> // for QFileInfoList
-#include <QLatin1String> // for QLatin1String
-#include <QList> // for QList
-#include <QScopedPointer> // for QScopedPointer
-#include <QString> // for QString, operator==
-#include <QStringList> // for QStringList
-#include <QTime> // for QTime
-#include <QVector> // for QVector
-#include <Qt> // for CaseInsensitive
-#include <QtGlobal> // for qPrintable, foreach
-
-#include "defs.h"
-#include "explorist_ini.h" // for explorist_ini_done, explorist_ini_get, mag_info
-#include "format.h" // for Format
-#include "gbfile.h" // for gbfclose, gbfeof, gbfgets, gbfopen, gbfwrite, gbfile
-#include "gbser.h" // for gbser_deinit, gbser_init, gbser_is_serial, gbser_read_line, gbser_set_port, gbser_write, gbser_OK
-#include "magellan.h" // for mm_meridian, mm_sportrak, magellan_icon_mapping_t, mm_gps315320, mm_unknown, mm_map330, mm_map410, pid_to_model_t, mm_gps310, m330_cleanse, mag_checksum, mag_find_descr_from_token, mag_find_token_from_descr, mag_rteparse, mag_trkparse
-#include "src/core/datetime.h" // for DateTime
-#include "vecs.h" // for Vecs
-
-
-static int bitrate = 4800;
-static int wptcmtcnt;
-static int wptcmtcnt_max;
-static int explorist;
-static int broken_sportrak;
-#define MYNAME "MAGPROTO"
-#define MAXCMTCT 200
-
-#define debug_serial (global_opts.debug_level > 1)
-
-static QString termread(char* ibuf, int size);
-static void termwrite(const char* obuf, int size);
-static void mag_readmsg(gpsdata_type objective);
-static void mag_handon();
-static void mag_handoff();
-static short_handle mkshort_handle = nullptr;
-static char* deficon = nullptr;
-static char* bs = nullptr;
-static char* cmts = nullptr;
-static char* noack = nullptr;
-static char* nukewpt = nullptr;
-static int route_out_count;
-static int waypoint_read_count;
-static int wpt_len = 8;
-static QString curfname;
-static int extension_hint;
-// For Explorist GC/510/610/710 families, bludgeon in GPX support.
-// (This has nothing to do with the Explorist 100...600 products.)
-static Format* gpx_vec;
-static mag_info* explorist_info;
-static QStringList os_gpx_files(const char* dirname);
-
-/*
- * Magellan's firmware is *horribly* slow to send the next packet after
- * we turn around an ack while we are reading from the device. It's
- * quite spiffy when we're writing to the device. Since we're *way*
- * less likely to lose data while reading from it than it is to lose data
- * when we write to it, we turn off the acks when we are predominantly
- * reading.
- */
-static int suppress_ack;
-
-enum mag_rxstate {
- mrs_handoff = 0,
- mrs_handon,
- mrs_awaiting_ack
-};
-
-/*
- * An individual element of a route.
- */
-struct mag_rte_elem {
- QString wpt_name;
- QString wpt_icon;
-};
-
-/*
- * A header of a route. Related elements of a route belong to this.
- */
-struct mag_rte_head_t {
- QList<mag_rte_elem*> elem_list; /* list of child rte_elems */
- char* rte_name{nullptr};
- int nelems{0};
-};
-
-static QList<Waypoint*> rte_wpt_tmp; /* temporary PGMNWPL msgs for routes */
-
-static gbfile* magfile_h;
-static mag_rxstate magrxstate;
-static int mag_error;
-static unsigned int last_rx_csum;
-static int found_done;
-static int got_version;
-static int is_file = 0;
-static route_head* trk_head;
-static int ignore_unable;
-
-static Waypoint* mag_wptparse(char*);
-using cleanse_fn = QString (const char*);
-static cleanse_fn* mag_cleanse;
-static const char** os_get_magellan_mountpoints();
-
-static const magellan_icon_mapping_t gps315_icon_table[] = {
- { "a", "filled circle" },
- { "b", "box" },
- { "c", "red buoy" },
- { "d", "green buoy" },
- { "e", "buoy" },
- { "f", "rocks" },
- { "g", "red daymark" },
- { "h", "green daymark" },
- { "i", "bell" },
- { "j", "danger" },
- { "k", "diver down" },
- { "l", "fish" },
- { "m", "house" },
- { "n", "mark" },
- { "o", "car" },
- { "p", "tent" },
- { "q", "boat" },
- { "r", "food" },
- { "s", "fuel" },
- { "t", "tree" },
- { nullptr, nullptr }
-};
-
-static const magellan_icon_mapping_t map330_icon_table[] = {
- { "a", "crossed square" },
- { "b", "box" },
- { "c", "house" },
- { "d", "aerial" },
- { "e", "airport" },
- { "f", "amusement park" },
- { "g", "ATM" },
- { "g", "Bank" },
- { "h", "auto repair" },
- { "i", "boating" },
- { "j", "camping" },
- { "k", "exit ramp" },
- { "l", "first aid" },
- { "m", "nav aid" },
- { "n", "buoy" },
- { "o", "fuel" },
- { "p", "garden" },
- { "q", "golf" },
- { "r", "hotel" },
- { "s", "hunting/fishing" },
- { "t", "large city" },
- { "u", "lighthouse" },
- { "v", "major city" },
- { "w", "marina" },
- { "x", "medium city" },
- { "y", "museum" },
- { "z", "obstruction" },
- { "aa", "park" },
- { "ab", "resort" },
- { "ac", "restaurant" },
- { "ad", "rock" },
- { "ae", "scuba" },
- { "af", "RV service" },
- { "ag", "shooting" },
- { "ah", "sight seeing" },
- { "ai", "small city" },
- { "aj", "sounding" },
- { "ak", "sports arena" },
- { "al", "tourist info" },
- { "am", "truck service" },
- { "an", "winery" },
- { "ao", "wreck" },
- { "ap", "zoo" },
- { "ah", "Virtual cache"}, /* Binos: because you "see" them. */
- { "ak", "Micro-Cache" }, /* Looks like a film canister. */
- { "an", "Multi-Cache"}, /* Winery: grapes 'coz they "bunch" */
- { "s", "Unknown Cache"}, /* 'Surprise' cache: use a target. */
- { "ac", "Event Cache"}, /* Event caches. May be food. */
- { nullptr, nullptr }
-};
-
-pid_to_model_t pid_to_model[] = {
- { mm_gps315320, 19, "ColorTrak" },
- { mm_gps315320, 24, "GPS 315/320" },
- { mm_map410, 25, "Map 410" },
- { mm_map330, 30, "Map 330" },
- { mm_gps310, 31, "GPS 310" },
- { mm_meridian, 33, "Meridian" },
- { mm_meridian, 35, "ProMark 2" },
- { mm_sportrak, 36, "SporTrak Map/Pro" },
- { mm_sportrak, 37, "SporTrak" },
- { mm_meridian, 38, "FX324 Plotter" },
- { mm_meridian, 39, "Meridian Color" },
- { mm_meridian, 40, "FX324C Plotter" },
- { mm_sportrak, 41, "Sportrak Color" },
- { mm_sportrak, 42, "Sportrak Marine" },
- { mm_meridian, 43, "Meridian Marine" },
- { mm_sportrak, 44, "Sportrak Topo" },
- { mm_sportrak, 45, "Mystic" },
- { mm_meridian, 46, "MobileMapper" },
- { mm_meridian, 110, "Explorist 100" },
- { mm_meridian, 111, "Explorist 200" },
- { mm_unknown, 0, nullptr }
-};
-
-static const magellan_icon_mapping_t* icon_mapping = map330_icon_table;
-
-/*
- * For each receiver type, return a "cleansed" version of the string
- * that's valid for a waypoint name or comment. The string should be
- * freed when you're done with it.
- */
-static QString
-m315_cleanse(const char* istring)
-{
- char* rstring = (char*) xmalloc(strlen(istring)+1);
- char* o;
- const char* i;
- static char m315_valid_chars[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789";
- for (o=rstring,i=istring; *i; i++) {
- if (strchr(m315_valid_chars, toupper(*i))) {
- *o++ = toupper(*i);
- }
- }
- *o = 0;
- QString rv(rstring);
- xfree(rstring);
- return rv;
-}
-
-/*
- * Do same for 330, Meridian, and SportTrak.
- */
-QString
-m330_cleanse(const char* istring)
-{
- static char m330_valid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ "
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789+-.'/!@#<%^&>()=:\\";
- char* rstring = (char*) xmalloc(strlen(istring)+1);
- char* o;
- const char* i;
-
- for (o=rstring,i=istring; *i; i++) {
- if (strchr(m330_valid_chars, *i)) {
- *o++ = *i;
- }
- }
- *o = 0;
- QString rv(rstring);
- xfree(rstring);
- return rv;
-}
-
-/*
- * Given a protocol message, compute the checksum as needed by
- * the Magellan protocol.
- */
-unsigned int
-mag_checksum(const char* const buf)
-{
- int csum = 0;
-
- for (const char* p = buf; *p; p++) {
- csum ^= *p;
- }
-
- return csum;
-}
-static unsigned int
-mag_pchecksum(const char* const buf, int len)
-{
- int csum = 0;
- const char* p = buf;
- for (; len ; len--) {
- csum ^= *p++;
- }
- return csum;
-}
-
-static void
-mag_writemsg(const char* const buf)
-{
- unsigned int osum = mag_checksum(buf);
- int retry_cnt = 5;
- QScopedPointer<char, QScopedPointerPodDeleter> obuf;
-
- if (debug_serial) {
- warning("WRITE: $%s*%02X\r\n",buf, osum);
- }
-
-retry:
-
- int i = xasprintf(obuf, "$%s*%02X\r\n",buf, osum);
- termwrite(obuf.data(), i);
- if (magrxstate == mrs_handon || magrxstate == mrs_awaiting_ack) {
- magrxstate = mrs_awaiting_ack;
- mag_readmsg(trkdata);
- if (last_rx_csum != osum) {
- if (debug_serial) {
- warning("COMM ERROR: Expected %02x, got %02x",
- osum, last_rx_csum);
- }
- if (retry_cnt--) {
- goto retry;
- } else {
- mag_handoff();
- fatal(MYNAME
- ": Too many communication errors.\n");
- }
- }
- }
-}
-
-static void
-mag_writeack(int osum)
-{
- QScopedPointer<char, QScopedPointerPodDeleter> nbuf;
- QScopedPointer<char, QScopedPointerPodDeleter> obuf;
-
- if (is_file) {
- return;
- }
-
- (void) xasprintf(nbuf, "PMGNCSM,%02X", osum);
- unsigned int nsum = mag_checksum(nbuf.data());
- int i = xasprintf(obuf, "$%s*%02X\r\n",nbuf.data(), nsum);
-
- if (debug_serial) {
- warning("ACK WRITE: %s",obuf.data());
- }
- /*
- * Don't call mag_writemsg here so we don't get into ack feedback
- * loops.
- */
- termwrite(obuf.data(), i);
-}
-
-static void
-mag_handon()
-{
- if (!is_file) {
- mag_writemsg("PMGNCMD,HANDON");
- }
- magrxstate = mrs_handon;
-
-}
-
-static void
-mag_handoff()
-{
- if (!is_file) {
- mag_writemsg("PMGNCMD,HANDOFF");
- }
- magrxstate = mrs_handoff;
-}
-
-static void
-mag_verparse(char* ibuf)
-{
- int prodid = mm_unknown;
- char version[1024];
- pid_to_model_t* pp = pid_to_model;
-
- got_version = 1;
- sscanf(ibuf,"$PMGNVER,%d,%[^,]", &prodid, version);
-
- for (pp = pid_to_model; pp->model != mm_unknown; pp++) {
- if (pp->pid == prodid) {
- break;
- }
- }
-
- if (prodid == 37) {
- broken_sportrak = 1;
- }
-
- switch (pp->model) {
- case mm_gps315320:
- case mm_map410:
- icon_mapping = gps315_icon_table;
- setshort_length(mkshort_handle, 6);
- setshort_mustupper(mkshort_handle, 1);
- mag_cleanse = m315_cleanse;
- break;
- case mm_map330:
- case mm_meridian:
- case mm_sportrak:
- icon_mapping = map330_icon_table;
- setshort_length(mkshort_handle, wpt_len);
- setshort_mustupper(mkshort_handle, 0);
- mag_cleanse = m330_cleanse;
- break;
- default:
- fatal(MYNAME ": Unknown receiver type %d, model version '%s'.\n", prodid, version);
- }
-}
-
-#define IS_TKN(x) (strncmp(ibuf,x, sizeof(x)-1) == 0)
-
-static void
-mag_readmsg(gpsdata_type objective)
-{
- char ibuf[512]; /* oliskoli: corrupted data (I've seen descr with a lot
- of escaped FFFFFFFF) may need more size */
- int retrycnt = 20;
-
-retry:
- QString gr = termread(ibuf, sizeof(ibuf));
-
- if (gr.isEmpty()) {
- if (!got_version) {
- /*
- * The 315 can take up to six seconds to respond to
- * a VERSION command. Since this is on startup,
- * we'll be fairly persistent in retrying.
- */
- if (retrycnt--) {
- goto retry;
- } else {
- fatal(MYNAME ": No data received from GPS.\n");
- }
- } else {
- if (is_file) {
- found_done = 1;
- }
- return;
- }
- }
-
- /* If column zero isn't a dollar sign, it's not for us */
- if (ibuf[0] != '$') {
- fatal(MYNAME ": line doesn't start with '$'.\n");
- }
-
-
- int isz = strlen(ibuf);
-
- if (isz < 5) {
- if (debug_serial) {
- warning("SHORT READ %d\n", isz);
- }
- return;
- }
- mag_error = 0;
- while (!isprint(ibuf[isz])) {
- isz--;
- }
- char* isump = &ibuf[isz-1];
- unsigned int isum = strtoul(isump, nullptr,16);
- if (isum != mag_pchecksum(&ibuf[1], isz-3)) {
- if (debug_serial) {
- warning("RXERR %02x/%02x: '%s'\n", isum, mag_pchecksum(&ibuf[1],isz-5), ibuf);
- }
- /* Special case receive errors early on. */
- if (!got_version) {
- fatal(MYNAME ": bad communication. Check bit rate.\n");
- }
- }
- if (debug_serial) {
- warning("READ: %s\n", ibuf);
- }
- if (IS_TKN("$PMGNCSM,")) {
- last_rx_csum = strtoul(&ibuf[9], nullptr, 16);
- magrxstate = mrs_handon;
- return;
- }
- if (strncmp(ibuf, "$PMGNWPL,", 7) == 0) {
- Waypoint* wpt = mag_wptparse(ibuf);
- waypoint_read_count++;
- if (global_opts.verbose_status) {
- waypt_status_disp(waypoint_read_count,
- waypoint_read_count);
- }
-
- if (extension_hint) {
- if (extension_hint == WPTDATAMASK) {
- waypt_add(wpt);
- } else if (extension_hint == RTEDATAMASK) {
- rte_wpt_tmp.append(wpt);
- }
- } else {
- switch (objective) {
- case wptdata:
- waypt_add(wpt);
- break;
- case rtedata:
- rte_wpt_tmp.append(wpt);
- break;
- default:
- break;
- }
- }
- }
- if (strncmp(ibuf, "$PMGNTRK,", 7) == 0) {
- Waypoint* wpt = mag_trkparse(ibuf);
- /*
- * Allow lazy allocation of track head.
- */
- if (trk_head == nullptr) {
- /* These tracks don't have names, so derive one
- * from input filename.
- */
-
- trk_head = new route_head;
-
- /* Whack trailing extension if present. */
- QString s = get_filename(curfname);
- int idx = s.indexOf('.');
- if (idx > 0) {
- s.truncate(idx);
- }
-
- trk_head->rte_name = s;
- track_add_head(trk_head);
- }
-
- track_add_wpt(trk_head, wpt);
- }
- if (strncmp(ibuf, "$PMGNRTE,", 7) == 0) {
- mag_rteparse(ibuf);
- }
- if (IS_TKN("$PMGNVER,")) {
- mag_verparse(ibuf);
- }
- mag_error = 0;
- if (!ignore_unable && IS_TKN("$PMGNCMD,UNABLE")) {
- warning("Unable to send\n");
- found_done = 1;
- mag_error = 1;
- ignore_unable = 0;
- return;
- }
- if (IS_TKN("$PMGNCMD,END") || (is_file && (gbfeof(magfile_h)))) {
- found_done = 1;
- return;
- }
-
- if (magrxstate != mrs_handoff) {
- mag_writeack(isum);
- }
-}
-
-static void* serial_handle = nullptr;
-
-static int
-terminit(const QString& portname, int create_ok)
-{
- if (gbser_is_serial(qPrintable(portname))) {
- if (serial_handle = gbser_init(qPrintable(portname)), nullptr != serial_handle) {
- int rc;
- if (rc = gbser_set_port(serial_handle, bitrate, 8, 0, 1), gbser_OK != rc) {
- fatal(MYNAME ": Can't configure port\n");
- }
- }
- is_file = 0;
- if (serial_handle == nullptr) {
- fatal(MYNAME ": Could not open serial port %s\n", qPrintable(portname));
- }
- return 1;
- } else {
- /* Does this check for an error? */
- magfile_h = gbfopen(portname, create_ok ? "w+b" : "rb", MYNAME);
- is_file = 1;
- icon_mapping = map330_icon_table;
- mag_cleanse = m330_cleanse;
- got_version = 1;
- return 0;
- }
-}
-
-static QString termread(char* ibuf, int size)
-{
- if (is_file) {
- return gbfgets(ibuf, size, magfile_h);
- } else {
- int rc = gbser_read_line(serial_handle, ibuf, size, 2000, 0x0a, 0x0d);
- if (rc != gbser_OK) {
- fatal(MYNAME ": Read error\n");
- }
- return ibuf;
- }
-}
-
-/* Though not documented in the protocol spec, if the unit itself
- * wants to create a field containing a comma, it will encode it
- * as <escape>2C. We extrapolate that any 2 digit hex encoding may
- * be valid. We don't do this in termread() since we need to do it
- * after the scanf. This means we have to do it field-by-field
- * basis.
- *
- * The buffer is modified in place and shortened by copying the remaining
- * string including the terminator.
- */
-static
-void
-mag_dequote(char* ibuf)
-{
- char* esc = nullptr;
-
- while ((esc = strchr(ibuf, 0x1b))) {
- int nremains = strlen(esc);
- if (nremains >= 3) {
- static const char hex[17] = "0123456789ABCDEF";
- const char* c1 = strchr(hex, esc[1]);
- const char* c2 = strchr(hex, esc[2]);
- if (c1 && c2) {
- int escv = (c1 - hex) * 16 + (c2 - hex);
- if (escv == 255) { /* corrupted data */
- char* tmp = esc + 1;
- while (*tmp == 'F') {
- tmp++;
- }
- memmove(esc, tmp, strlen(tmp) + 1);
- } else {
- *esc++ = (isprint(escv)) ? escv : '$';
- /* buffers overlap */
- memmove(esc, esc+2, nremains - 2);
- }
- }
- } else {
- *esc = '\0'; /* trim corrupted data,
- otherwise we get an endless loop */
- }
- }
-}
-
-static void
-termwrite(const char* obuf, int size)
-{
- if (is_file) {
- size_t nw;
- if (nw = gbfwrite(obuf, 1, size, magfile_h), nw < (size_t) size) {
- fatal(MYNAME ": Write error");
- }
- } else {
- int rc;
- if (rc = gbser_write(serial_handle, obuf, size), rc < 0) {
- fatal(MYNAME ": Write error");
- }
- }
-}
-
-static void termdeinit()
-{
- if (is_file) {
- gbfclose(magfile_h);
- magfile_h = nullptr;
- } else {
- gbser_deinit(serial_handle);
- serial_handle = nullptr;
- }
-}
-
-/*
- * Arg tables are doubled up so that -? can output appropriate help
- */
-static
-QVector<arglist_t> mag_sargs = {
- {
- "deficon", &deficon, "Default icon name", nullptr, ARGTYPE_STRING,
- ARG_NOMINMAX, nullptr
- },
- {
- "maxcmts", &cmts, "Max number of comments to write (maxcmts=200)",
- "200", ARGTYPE_INT, ARG_NOMINMAX, nullptr
- },
- {
- "baud", &bs, "Numeric value of bitrate (baud=4800)", "4800",
- ARGTYPE_INT, ARG_NOMINMAX, nullptr
- },
- {
- "noack", &noack, "Suppress use of handshaking in name of speed",
- nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
- },
- {
- "nukewpt", &nukewpt, "Delete all waypoints", nullptr, ARGTYPE_BOOL,
- ARG_NOMINMAX, nullptr
- },
-};
-
-static
-QVector<arglist_t> mag_fargs = {
- {
- "deficon", &deficon, "Default icon name", nullptr, ARGTYPE_STRING,
- ARG_NOMINMAX, nullptr
- },
- {
- "maxcmts", &cmts, "Max number of comments to write (maxcmts=200)",
- nullptr, ARGTYPE_INT, ARG_NOMINMAX, nullptr
- },
-};
-
-/*
- * The part of the serial init that's common to read and write.
- */
-static void
-mag_serial_init_common(const QString& portname)
-{
- if (is_file) {
- return;
- }
-
- mag_handoff();
- if (!noack && !suppress_ack) {
- mag_handon();
- }
-
- time_t now = current_time().toTime_t();
- /*
- * The 315 can take up to 4.25 seconds to respond to initialization
- * commands. Time out on the side of caution.
- */
- time_t later = now + 6;
- got_version = 0;
- mag_writemsg("PMGNCMD,VERSION");
-
- while (!got_version) {
- mag_readmsg(trkdata);
- if (current_time().toTime_t() > later) {
- fatal(MYNAME ": No acknowledgment from GPS on %s\n",
- qPrintable(portname));
- }
- }
-
- if ((icon_mapping != gps315_icon_table)) {
- /*
- * The 315 can't handle this command, so we set a global
- * to ignore the NAK on it.
- */
- ignore_unable = 1;
- mag_writemsg("PMGNCMD,NMEAOFF");
- ignore_unable = 0;
- }
-
- if (nukewpt) {
- /* The unit will send us an "end" message upon completion */
- mag_writemsg("PMGNCMD,DELETE,WAYPOINT");
- mag_readmsg(trkdata);
- if (!found_done) {
- fatal(MYNAME ": Unexpected response to waypoint delete command.\n");
- }
- found_done = 0;
- }
-
-}
-static void
-mag_rd_init_common(const QString& portname)
-{
- waypoint_read_count = 0;
- // For Explorist GC, intercept the device access and redirect to GPX.
- // We actually do the rd_init() inside read as we may have multiple
- // files that we have to read.
- if (portname == "usb:") {
- const char** dlist = os_get_magellan_mountpoints();
- explorist_info = explorist_ini_get(dlist);
- if (explorist_info) {
- gpx_vec = Vecs::Instance().find_vec("gpx");
- }
- return;
- }
-
- if (bs) {
- bitrate=xstrtoi(bs, nullptr, 10);
- }
-
- if (!mkshort_handle) {
- mkshort_handle = mkshort_new_handle();
- }
-
- terminit(portname, 0);
- mag_serial_init_common(portname);
-
- rte_wpt_tmp.clear();
-
- /* find the location of the tail of the path name,
- * make a copy of it, then lop off the file extension
- */
-
- curfname = get_filename(portname);
-
- /*
- * I'd rather not derive behaviour from filenames but since
- * we can't otherwise tell if we should put a WPT on the route
- * queue or the WPT queue in the presence of (-w -r -t) we
- * divine a hint from the filename extension when we can.
- */
- QString exten = QFileInfo(curfname).suffix();
- if (exten.length() > 0) {
- if (0 == exten.compare(QLatin1String("upt"), Qt::CaseInsensitive)) {
- extension_hint = WPTDATAMASK;
- } else if (0 == exten.compare(QLatin1String("log"), Qt::CaseInsensitive)) {
- extension_hint = TRKDATAMASK;
- } else if (0 == exten.compare(QLatin1String("rte"), Qt::CaseInsensitive)) {
- extension_hint = RTEDATAMASK;
- }
- }
-
-}
-
-static void
-mag_rd_init(const QString& portname)
-{
- explorist = 0;
- suppress_ack = 1;
- mag_rd_init_common(portname);
-}
-
-static void
-magX_rd_init(const QString& portname)
-{
- explorist = 1;
- mag_rd_init_common(portname);
-}
-
-static void
-mag_wr_init_common(const QString& portname)
-{
- suppress_ack = 0;
- if (bs) {
- bitrate=xstrtoi(bs, nullptr, 10);
- }
-
- if (waypt_count() > 500) {
- fatal(MYNAME ": Meridian/Explorist does not support more than 500 waypoints in one file. Only\n200 waypoints may have comments.\nDecrease the number of waypoints sent.\n");
- }
-
- if (cmts) {
- wptcmtcnt_max = xstrtoi(cmts, nullptr, 10);
- } else {
- wptcmtcnt_max = MAXCMTCT ;
- }
-
- if (!mkshort_handle) {
- mkshort_handle = mkshort_new_handle();
- }
-
- terminit(portname, 1);
- mag_serial_init_common(portname);
-
- rte_wpt_tmp.clear();
-}
-
-/*
- * Entry point for extended (explorist) points.
- */
-static void
-magX_wr_init(const QString& portname)
-{
- wpt_len = 20;
- explorist = 1;
- mag_wr_init_common(portname);
- setshort_length(mkshort_handle, wpt_len);
- setshort_whitespace_ok(mkshort_handle, 1);
-}
-
-static void
-mag_wr_init(const QString& portname)
-{
- explorist = 0;
- wpt_len = 8;
- mag_wr_init_common(portname);
- /*
- * Whitespace is actually legal, but since waypoint name length is
- * only 8 bytes, we'll conserve them.
- */
-
- setshort_whitespace_ok(mkshort_handle, 0);
-}
-
-static void
-mag_deinit()
-{
- if (explorist_info) {
- explorist_ini_done(explorist_info);
- return;
- }
- mag_handoff();
- termdeinit();
- if (mkshort_handle) {
- mkshort_del_handle(&mkshort_handle);
- }
-
- while (!rte_wpt_tmp.isEmpty()) {
- delete rte_wpt_tmp.takeFirst();
- }
-
- trk_head = nullptr;
-
- curfname.clear();
-}
-
-static void
-mag_wr_deinit()
-{
- if (explorist) {
- mag_writemsg("PMGNCMD,END");
- }
- mag_deinit();
-}
-
-/*
- * I'm tired of arguing with scanf about optional fields . Detokenize
- * an incoming string that may contain empty fields.
- *
- * Probably should be cleaned up and moved to common code, but
- * making it deal with an arbitrary number of fields of arbitrary
- * size is icky. We don't have to solve the general case here...
- */
-
-static char ifield[20][100];
-static
-void parse_istring(char* istring)
-{
- int f = 0;
- int n;
- while (istring[0]) {
- char* fp = ifield[f];
- int x = sscanf(istring, "%[^,]%n", fp, &n);
- f++;
- if (x) {
- istring += n;
- /* IF more in this string, skip delim */
- if (istring[0]) {
- istring++;
- }
- } else {
- istring ++;
- }
- }
-}
-
-/*
- * Given an incoming track messages of the form:
- * $PMGNTRK,3605.259,N,08644.389,W,00151,M,201444.61,A,,020302*66
- * create and return a populated waypoint.
- */
-Waypoint*
-mag_trkparse(char* trkmsg)
-{
- int hms;
- int fracsecs;
- struct tm tm;
-
- auto* waypt = new Waypoint;
-
- memset(&tm, 0, sizeof(tm));
-
- /*
- * As some of the fields are optional, sscanf works badly
- * for us.
- */
- parse_istring(trkmsg);
- double latdeg = strtod(ifield[1], nullptr);
- char latdir = ifield[2][0];
- double lngdeg = strtod(ifield[3], nullptr);
- char lngdir = ifield[4][0];
- int alt = strtod(ifield[5], nullptr);
- char altunits = ifield[6][0];
- (void)altunits;
- sscanf(ifield[7], "%d.%d", &hms, &fracsecs);
- /* Field 8 is constant */
- /* Field nine is optional track name */
- int dmy = xstrtoi(ifield[10], nullptr, 10);
- int sec = hms % 100;
- hms = hms / 100;
- int min = hms % 100;
- hms = hms / 100;
- int hour = hms % 100;
-
- int year = 100 + dmy % 100 + 1900;
- dmy = dmy / 100;
- int mon = dmy % 100;
- dmy = dmy / 100;
- int day = dmy % 100;
- QDateTime dt(QDate(year, mon, day), QTime(hour, min, sec, fracsecs * 10), Qt::UTC);
- waypt->SetCreationTime(dt);
-
- if (latdir == 'S') {
- latdeg = -latdeg;
- }
- waypt->latitude = ddmm2degrees(latdeg);
-
- if (lngdir == 'W') {
- lngdeg = -lngdeg;
- }
- waypt->longitude = ddmm2degrees(lngdeg);
-
- waypt->altitude = alt;
-
- return waypt;
-
-}
-
-/*
- * Given an incoming route messages of the form:
- * $PMGNRTE,4,1,c,1,DAD,a,Anna,a*61
- * generate a route.
- */
-void
-mag_rteparse(char* rtemsg)
-{
- int n;
- int frags,frag,rtenum;
- char xbuf[100],next_stop[100],abuf[100];
- char* currtemsg;
- static mag_rte_head_t* mag_rte_head;
-
-#if 0
- sscanf(rtemsg,"$PMGNRTE,%d,%d,%c,%d%n",
- &frags,&frag,xbuf,&rtenum,&n);
-#else
- sscanf(rtemsg,"$PMGNRTE,%d,%d,%c,%d%n",
- &frags,&frag,xbuf,&rtenum,&n);
-
- /* Explorist has a route name here */
- QString rte_name;
- if (explorist) {
- char* ca = rtemsg + n;
- if (*ca++ != ',') {
- fatal(MYNAME ": Incorrectly formatted route line '%s'", rtemsg);
- }
-
- char* ce = strchr(ca, ',');
- if (ce == nullptr) {
- fatal(MYNAME ": Incorrectly formatted route line '%s'", rtemsg);
- }
-
- if (ca == ce) {
- rte_name = "Route";
- rte_name += QString::number(rtenum);
- } else {
- rte_name = ca;
- rte_name.truncate(ce-ca);
- }
-
- n += ((ce - ca) + 1);
- }
-
-#endif
-
- /*
- * This is the first component of a route. Allocate a new
- * head.
- */
- if (frag == 1) {
- mag_rte_head = new mag_rte_head_t;
- mag_rte_head->nelems = frags;
- }
-
- currtemsg = rtemsg + n;
-
- /*
- * The individual line may contain several route elements.
- * loop and pick those up.
- */
- while (sscanf(currtemsg,",%[^,],%[^,]%n",next_stop, abuf,&n)) {
- if ((next_stop[0] == 0) || (next_stop[0] == '*')) {
- break;
- }
-
- /* trim CRC from waypoint icon string */
- if (char* p = strchr(abuf, '*'); p != nullptr) {
- *p = '\0';
- }
-
- auto* rte_elem = new mag_rte_elem;
-
- rte_elem->wpt_name = next_stop;
- rte_elem->wpt_icon = abuf;
-
- mag_rte_head->elem_list.append(rte_elem);
-
- /* Sportrak (the non-mapping unit) creates malformed
- * RTE sentence with no icon info after the routepoint
- * name. So if we saw an "icon" treat that as new
- * routepoint.
- */
- if (broken_sportrak && abuf[0]) {
- rte_elem = new mag_rte_elem;
- rte_elem->wpt_name = abuf;
-
- mag_rte_head->elem_list.append(rte_elem);
- }
-
- next_stop[0] = 0;
- currtemsg += n;
- }
-
- /*
- * If this was the last fragment of the route, add it to the
- * gpsbabel internal structs now.
- */
- if (frag == mag_rte_head->nelems) {
-
- auto* rte_head = new route_head;
- route_add_head(rte_head);
- rte_head->rte_num = rtenum;
- rte_head->rte_name = rte_name;
-
- /*
- * It is quite feasible that we have 200 waypoints,
- * 3 of which are used in the route. We'll need to find
- * those in the queue for SD routes...
- */
-
- while (!mag_rte_head->elem_list.isEmpty()) {
- mag_rte_elem* re = mag_rte_head->elem_list.takeFirst();
-
- /*
- * Copy route points from temp wpt queue.
- */
- foreach (const Waypoint* waypt, rte_wpt_tmp) {
- if (waypt->shortname == re->wpt_name) {
- auto* wpt = new Waypoint(*waypt);
- route_add_wpt(rte_head, wpt);
- break;
- }
- }
-
- delete re;
- }
- delete mag_rte_head;
- }
-}
-
-QString
-mag_find_descr_from_token(const char* token)
-{
- if (icon_mapping == nullptr) {
- return "unknown";
- }
-
- for (const magellan_icon_mapping_t* i = icon_mapping; i->token; i++) {
- if (token[0] == 0) {
- break;
- }
- if (case_ignore_strcmp(token, i->token) == 0) {
- return i->icon;
- }
- }
- return icon_mapping[0].icon;
-}
-
-QString
-mag_find_token_from_descr(const QString& icon)
-{
- const magellan_icon_mapping_t* i = icon_mapping;
-
- if (i == nullptr || icon == nullptr) {
- return "a";
- }
-
- for (i = icon_mapping; i->token; i++) {
- if (icon.compare(i->icon, Qt::CaseInsensitive) == 0) {
- return i->token;
- }
- }
- return icon_mapping[0].token;
-}
-
-/*
- * Given an incoming waypoint messages of the form:
- * $PMGNWPL,3549.499,N,08650.827,W,0000257,M,HOME,HOME,c*4D
- * create and return a populated waypoint.
- */
-static Waypoint*
-mag_wptparse(char* trkmsg)
-{
- double latdeg, lngdeg;
- char latdir;
- char lngdir;
- int alt;
- char altunits;
- char shortname[100];
- char descr[256];
- char icon_token[100];
- int i = 0;
-
- descr[0] = 0;
- icon_token[0] = 0;
-
- auto* waypt = new Waypoint;
-
- sscanf(trkmsg,"$PMGNWPL,%lf,%c,%lf,%c,%d,%c,%[^,],%[^,]",
- &latdeg,&latdir,
- &lngdeg,&lngdir,
- &alt,&altunits,shortname,descr);
- char* icone = strrchr(trkmsg, '*');
- char* icons = strrchr(trkmsg, ',')+1;
-
- mag_dequote(descr);
-
- for (char* blah = icons ; blah < icone; blah++) {
- icon_token[i++] = *blah;
- }
- icon_token[i++] = '\0';
-
- if (latdir == 'S') {
- latdeg = -latdeg;
- }
- waypt->latitude = ddmm2degrees(latdeg);
-
- if (lngdir == 'W') {
- lngdeg = -lngdeg;
- }
- waypt->longitude = ddmm2degrees(lngdeg);
-
- waypt->altitude = alt;
- waypt->shortname = shortname;
- waypt->description = descr;
- waypt->icon_descr = mag_find_descr_from_token(icon_token);
-
- return waypt;
-}
-
-static void
-mag_read()
-{
- if (gpx_vec) {
- QStringList f = os_gpx_files(explorist_info->track_path);
- for (const auto& file : qAsConst(f)) {
- gpx_vec->rd_init(file);
- gpx_vec->read();
- gpx_vec->rd_deinit();
- }
-
- f = os_gpx_files(explorist_info->waypoint_path);
- for (const auto& file : qAsConst(f)) {
- gpx_vec->rd_init(file);
- gpx_vec->read();
- gpx_vec->rd_deinit();
- }
-#if 0
- f = os_gpx_files(explorist_info->geo_path);
- for (const auto& file : qAsConst(f)) {
- gpx_vec->rd_init(file);
- gpx_vec->read();
- gpx_vec->rd_deinit();
- }
-#endif
- return;
- }
-
- found_done = 0;
- if (global_opts.masked_objective & TRKDATAMASK) {
- magrxstate = mrs_handoff;
- if (!is_file) {
- mag_writemsg("PMGNCMD,TRACK,2");
- }
-
- while (!found_done) {
- mag_readmsg(trkdata);
- }
- }
-
- found_done = 0;
- if (global_opts.masked_objective & WPTDATAMASK) {
- magrxstate = mrs_handoff;
- if (!is_file) {
- mag_writemsg("PMGNCMD,WAYPOINT");
- }
-
- while (!found_done) {
- mag_readmsg(wptdata);
- }
- }
-
- found_done = 0;
- if (global_opts.masked_objective & RTEDATAMASK) {
- magrxstate = mrs_handoff;
- if (!is_file) {
- /*
- * serial routes require waypoint & routes
- * messages commands.
- */
- mag_writemsg("PMGNCMD,WAYPOINT");
-
- while (!found_done) {
- mag_readmsg(rtedata);
- }
-
- mag_writemsg("PMGNCMD,ROUTE");
-
- found_done = 0;
- while (!found_done) {
- mag_readmsg(rtedata);
- }
- } else {
- /*
- * SD routes are a stream of PMGNWPL and
- * PMGNRTE messages, in that order.
- */
- while (!found_done) {
- mag_readmsg(rtedata);
- }
- }
- }
-}
-
-static
-void
-mag_waypt_pr(const Waypoint* waypointp)
-{
- QScopedPointer<char, QScopedPointerPodDeleter> obuf;
- QScopedPointer<char, QScopedPointerPodDeleter> ofmtdesc;
- QString icon_token;
-
- double ilat = waypointp->latitude;
- double ilon = waypointp->longitude;
-
- double lon = fabs(ilon);
- double lat = fabs(ilat);
-
- int lon_deg = lon;
- int lat_deg = lat;
-
- lon = (lon - lon_deg) * 60.0;
- lat = (lat - lat_deg) * 60.0;
-
- lon = (lon_deg * 100.0 + lon);
- lat = (lat_deg * 100.0 + lat);
-
- if (deficon) {
- icon_token = mag_find_token_from_descr(deficon);
- } else {
- icon_token = mag_find_token_from_descr(waypointp->icon_descr);
- }
-
- if (!get_cache_icon(waypointp).isEmpty()) {
- icon_token = mag_find_token_from_descr(get_cache_icon(waypointp));
- }
-
- QString isrc = waypointp->notes.isEmpty() ? waypointp->description : waypointp->notes;
- QString owpt = global_opts.synthesize_shortnames ?
- mkshort_from_wpt(mkshort_handle, waypointp) : waypointp->shortname;
- QString odesc = isrc;
- owpt = mag_cleanse(CSTRc(owpt));
-
- if (global_opts.smart_icons &&
- waypointp->gc_data->diff && waypointp->gc_data->terr) {
- // It's a string and compactness counts, so "1.0" is OK to be "10".
- xasprintf(ofmtdesc, "%ud/%ud %s", waypointp->gc_data->diff,
- waypointp->gc_data->terr, CSTRc(odesc));
- odesc = mag_cleanse(ofmtdesc.data());
- } else {
- odesc = mag_cleanse(CSTRc(odesc));
- }
-
- /*
- * For the benefit of DirectRoute (which uses waypoint comments
- * to deliver turn-by-turn popups for street routing) allow a
- * cap on the comments delivered so we leave space for it to route.
- */
- if (!odesc.isEmpty() && (wptcmtcnt++ >= wptcmtcnt_max)) {
- odesc.clear();
- }
-
- xasprintf(obuf, "PMGNWPL,%4.3f,%c,%09.3f,%c,%07.0f,M,%-.*s,%-.46s,%s",
- lat, ilat < 0 ? 'S' : 'N',
- lon, ilon < 0 ? 'W' : 'E',
- waypointp->altitude == unknown_alt ?
- 0 : waypointp->altitude,
- wpt_len,
- CSTRc(owpt),
- CSTRc(odesc),
- CSTR(icon_token));
- mag_writemsg(obuf.data());
-
- if (!is_file) {
- if (mag_error) {
- warning("Protocol error Writing '%s'\n", obuf.data());
- }
- }
-}
-
-static
-void mag_track_disp(const Waypoint* waypointp)
-{
- QScopedPointer<char, QScopedPointerPodDeleter> obuf;
-
- double ilat = waypointp->latitude;
- double ilon = waypointp->longitude;
-
- QByteArray dmy("");
- QByteArray hms("");
- if (waypointp->creation_time.isValid()) {
- // Round to hundredths of seconds before conversion to string.
- // Rounding can ripple all the way from the msec to the year.
- QDateTime dt = waypointp->GetCreationTime().toUTC();
- dt = dt.addMSecs(10 * lround(dt.time().msec()/10.0) - dt.time().msec());
- assert((dt.time().msec() % 10) == 0);
- dmy = dt.toString(u"ddMMyy").toUtf8();
- hms = dt.toString(u"hhmmss.zzz").left(9).toUtf8();
- }
-
- double lon = fabs(ilon);
- double lat = fabs(ilat);
-
- int lon_deg = lon;
- int lat_deg = lat;
-
- lon = (lon - lon_deg) * 60.0;
- lat = (lat - lat_deg) * 60.0;
-
- lon = (lon_deg * 100.0 + lon);
- lat = (lat_deg * 100.0 + lat);
-
- xasprintf(obuf,"PMGNTRK,%4.3f,%c,%09.3f,%c,%05.0f,%c,%s,A,,%s",
- lat, ilat < 0 ? 'S' : 'N',
- lon, ilon < 0 ? 'W' : 'E',
- waypointp->altitude == unknown_alt ?
- 0 : waypointp->altitude,
- 'M', hms.constData(), dmy.constData());
- mag_writemsg(obuf.data());
-}
-
-static
-void mag_track_pr()
-{
- track_disp_all(nullptr, nullptr, mag_track_disp);
-}
-
-/*
-The spec says to stack points:
- $PMGNRTE,2,1,c,1,FOO,POINT1,b,POINT2,c,POINT3,d*6C<CR><LF>
-
-Meridian SD card and serial (at least) writes in pairs:
- $PMGNRTE,4,1,c,1,HOME,c,I49X73,a*15
- ...
- $PMGNRTE,4,4,c,1,RON273,a,MYCF93,a*7B
-
-The spec also says that some units don't like single-legged pairs,
-and to replace the 2nd name with "<<>>", but I haven't seen one of those.
-*/
-
-static void
-mag_route_trl(const route_head* rte)
-{
- QScopedPointer<char, QScopedPointerPodDeleter> obuff;
- QString buff1;
- QString buff2;
- QString icon_token;
-
- /* count waypoints for this route */
- int i = rte->rte_waypt_ct();
-
- /* number of output PMGNRTE messages at 2 points per line */
- int numlines = (i / 2) + (i % 2);
-
- /* increment the route counter. */
- route_out_count++;
-
- int thisline = i = 0;
- foreach (const Waypoint* waypointp, rte->waypoint_list) {
- i++;
-
- if (deficon) {
- icon_token = mag_find_token_from_descr(deficon);
- } else {
- icon_token = mag_find_token_from_descr(waypointp->icon_descr);
- }
-
- QString* pbuff;
- if (i == 1) {
- pbuff = &buff1;
- } else {
- pbuff = &buff2;
- }
- // Write name, icon tuple into alternating buff1/buff2 buffer.
- *pbuff = waypointp->shortname + ',' + icon_token;
-
- if ((waypointp == rte->waypoint_list.back()) || ((i % 2) == 0)) {
- QString expbuf;
- thisline++;
- if (explorist) {
- expbuf = rte->rte_name + ',';
- }
-
- xasprintf(obuff, "PMGNRTE,%d,%d,c,%d,%s%s,%s",
- numlines, thisline,
- rte->rte_num ? rte->rte_num : route_out_count,
- CSTRc(expbuf),
- CSTR(buff1), CSTR(buff2));
-
- mag_writemsg(obuff.data());
- buff1.clear();
- buff2.clear();
- i = 0;
- }
- }
-}
-
-static void
-mag_route_pr()
-{
- route_out_count = 0;
- route_disp_all(nullptr, mag_route_trl, mag_waypt_pr);
-
-}
-
-static void
-mag_write()
-{
-
- wptcmtcnt = 0;
-
- switch (global_opts.objective) {
- case trkdata:
- mag_track_pr();
- break;
- case wptdata:
- waypt_disp_all(mag_waypt_pr);
- break;
- case rtedata:
- mag_route_pr();
- break;
- default:
- fatal(MYNAME ": Unknown objective.\n");
- }
-}
-
-const char** os_get_magellan_mountpoints()
-{
-#if __APPLE__
- const char** dlist = (const char**) xcalloc(2, sizeof *dlist);
- dlist[0] = xstrdup("/Volumes/Magellan");
- dlist[1] = nullptr;
- return dlist;
-#else
- fatal("Not implemented");
- return nullptr;
-#endif
-}
-
-static QStringList
-os_gpx_files(const char* dirname)
-{
- QDir dir(dirname);
-
- const QFileInfoList filist = dir.entryInfoList(QStringList("*.gpx"), QDir::Files | QDir::Readable, QDir::Name);
- QStringList rv;
- for (const auto& fi : filist) {
- rv.append(fi.absoluteFilePath());
- }
- return rv;
-}
-
-/*
- * This is repeated just so it shows up as separate menu options
- * for the benefit of GUI wrappers.
- */
-ff_vecs_t mag_svecs = {
- ff_type_serial,
- FF_CAP_RW_ALL,
- mag_rd_init,
- mag_wr_init,
- mag_deinit,
- mag_deinit,
- mag_read,
- mag_write,
- nullptr,
- &mag_sargs,
- CET_CHARSET_ASCII, 0, /* CET-REVIEW */
- NULL_POS_OPS
-};
-
-ff_vecs_t mag_fvecs = {
- ff_type_file,
- FF_CAP_RW_ALL,
- mag_rd_init,
- mag_wr_init,
- mag_deinit,
- mag_deinit,
- mag_read,
- mag_write,
- nullptr,
- &mag_fargs,
- CET_CHARSET_ASCII, 0, /* CET-REVIEW */
- NULL_POS_OPS
-};
-
-/*
- * Extended (Explorist) entry tables.
- */
-ff_vecs_t magX_fvecs = {
- ff_type_file,
- FF_CAP_RW_ALL,
- magX_rd_init,
- magX_wr_init,
- mag_deinit,
- mag_wr_deinit,
- mag_read,
- mag_write,
- nullptr,
- &mag_fargs,
- CET_CHARSET_ASCII, 0, /* CET-REVIEW */
- NULL_POS_OPS
-};
99.9720 905.512
100.1892 902.231
100.5724 882.546
-102.0104 882.546
+102.0103 882.546
102.8041 875.984
103.0804 872.703
107.3975 921.916
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<gpx version="1.0" creator="GPSBabel - https://www.gpsbabel.org" xmlns="http://www.topografix.com/GPX/1/0">
+ <time>2022-12-28T08:23:44.990Z</time>
+ <bounds minlat="41.154633333" minlon="-85.165883333" maxlat="41.643716667" maxlon="-82.738933333"/>
+ <trk>
+ <name>dusky</name>
+ <trkseg>
+ <trkpt lat="41.383183333" lon="-82.738933333">
+ <ele>190.000</ele>
+ <time>2004-08-17T03:07:30.190Z</time>
+ </trkpt>
+ <trkpt lat="41.342766667" lon="-82.758350000">
+ <ele>217.000</ele>
+ <time>2004-08-17T03:10:55.190Z</time>
+ </trkpt>
+ <trkpt lat="41.343083333" lon="-82.764366667">
+ <ele>218.000</ele>
+ <time>2004-08-17T03:11:37.190Z</time>
+ </trkpt>
+ <trkpt lat="41.341883333" lon="-82.770550000">
+ <ele>218.000</ele>
+ <time>2004-08-17T03:12:00.200Z</time>
+ </trkpt>
+ <trkpt lat="41.347550000" lon="-82.806500000">
+ <ele>215.000</ele>
+ <time>2004-08-17T03:13:34.190Z</time>
+ </trkpt>
+ <trkpt lat="41.354783333" lon="-82.880766667">
+ <ele>201.000</ele>
+ <time>2004-08-17T03:16:45.200Z</time>
+ </trkpt>
+ <trkpt lat="41.410050000" lon="-83.141416667">
+ <ele>185.000</ele>
+ <time>2004-08-17T03:28:17.220Z</time>
+ </trkpt>
+ <trkpt lat="41.421083333" lon="-83.167333333">
+ <ele>186.000</ele>
+ <time>2004-08-17T03:29:33.220Z</time>
+ </trkpt>
+ <trkpt lat="41.453516667" lon="-83.273933333">
+ <ele>190.000</ele>
+ <time>2004-08-17T03:34:26.230Z</time>
+ </trkpt>
+ <trkpt lat="41.465083333" lon="-83.298083333">
+ <ele>188.000</ele>
+ <time>2004-08-17T03:35:39.230Z</time>
+ </trkpt>
+ <trkpt lat="41.473900000" lon="-83.332266667">
+ <ele>192.000</ele>
+ <time>2004-08-17T03:37:11.230Z</time>
+ </trkpt>
+ <trkpt lat="41.509716667" lon="-83.411850000">
+ <ele>195.000</ele>
+ <time>2004-08-17T03:41:07.240Z</time>
+ </trkpt>
+ <trkpt lat="41.513600000" lon="-83.430383333">
+ <ele>196.000</ele>
+ <time>2004-08-17T03:41:56.240Z</time>
+ </trkpt>
+ <trkpt lat="41.537833333" lon="-83.494216667">
+ <ele>195.000</ele>
+ <time>2004-08-17T03:44:58.240Z</time>
+ </trkpt>
+ <trkpt lat="41.541583333" lon="-83.519666667">
+ <ele>195.000</ele>
+ <time>2004-08-17T03:46:04.240Z</time>
+ </trkpt>
+ <trkpt lat="41.578583333" lon="-83.603633333">
+ <ele>196.000</ele>
+ <time>2004-08-17T03:50:12.250Z</time>
+ </trkpt>
+ <trkpt lat="41.583033333" lon="-83.611483333">
+ <ele>187.000</ele>
+ <time>2004-08-17T03:50:37.260Z</time>
+ </trkpt>
+ <trkpt lat="41.590650000" lon="-83.642066667">
+ <ele>199.000</ele>
+ <time>2004-08-17T03:51:59.250Z</time>
+ </trkpt>
+ <trkpt lat="41.595416667" lon="-83.782400000">
+ <ele>202.000</ele>
+ <time>2004-08-17T03:57:57.270Z</time>
+ </trkpt>
+ <trkpt lat="41.602716667" lon="-83.811050000">
+ <ele>213.000</ele>
+ <time>2004-08-17T03:59:14.280Z</time>
+ </trkpt>
+ <trkpt lat="41.598300000" lon="-84.012083333">
+ <ele>225.000</ele>
+ <time>2004-08-17T04:07:51.280Z</time>
+ </trkpt>
+ <trkpt lat="41.592716667" lon="-84.027233333">
+ <ele>233.000</ele>
+ <time>2004-08-17T04:08:34.280Z</time>
+ </trkpt>
+ <trkpt lat="41.591050000" lon="-84.325250000">
+ <ele>220.000</ele>
+ <time>2004-08-17T04:21:19.300Z</time>
+ </trkpt>
+ <trkpt lat="41.587600000" lon="-84.340950000">
+ <ele>221.000</ele>
+ <time>2004-08-17T04:22:00.300Z</time>
+ </trkpt>
+ <trkpt lat="41.603716667" lon="-84.391116667">
+ <ele>233.000</ele>
+ <time>2004-08-17T04:24:19.300Z</time>
+ </trkpt>
+ <trkpt lat="41.614016667" lon="-84.545983333">
+ <ele>275.000</ele>
+ <time>2004-08-17T04:30:59.320Z</time>
+ </trkpt>
+ <trkpt lat="41.615350000" lon="-84.548083333">
+ <ele>276.000</ele>
+ <time>2004-08-17T04:31:10.320Z</time>
+ </trkpt>
+ <trkpt lat="41.613066667" lon="-84.549383333">
+ <ele>276.000</ele>
+ <time>2004-08-17T04:31:29.320Z</time>
+ </trkpt>
+ <trkpt lat="41.613066667" lon="-84.553583333">
+ <ele>275.000</ele>
+ <time>2004-08-17T04:32:13.320Z</time>
+ </trkpt>
+ <trkpt lat="41.618583333" lon="-84.552900000">
+ <ele>269.000</ele>
+ <time>2004-08-17T04:32:46.330Z</time>
+ </trkpt>
+ <trkpt lat="41.639366667" lon="-84.553533333">
+ <ele>269.000</ele>
+ <time>2004-08-17T04:34:25.320Z</time>
+ </trkpt>
+ <trkpt lat="41.643450000" lon="-84.567883333">
+ <ele>267.000</ele>
+ <time>2004-08-17T04:35:21.320Z</time>
+ </trkpt>
+ <trkpt lat="41.643716667" lon="-84.573216667">
+ <ele>266.000</ele>
+ <time>2004-08-17T04:35:41.330Z</time>
+ </trkpt>
+ <trkpt lat="41.643333333" lon="-84.656733333">
+ <ele>281.000</ele>
+ <time>2004-08-17T04:40:11.330Z</time>
+ </trkpt>
+ <trkpt lat="41.641033333" lon="-84.666400000">
+ <ele>279.000</ele>
+ <time>2004-08-17T04:40:42.340Z</time>
+ </trkpt>
+ <trkpt lat="41.637316667" lon="-84.741400000">
+ <ele>298.000</ele>
+ <time>2004-08-17T04:45:08.340Z</time>
+ </trkpt>
+ <trkpt lat="41.635333333" lon="-84.751766667">
+ <ele>297.000</ele>
+ <time>2004-08-17T04:45:55.330Z</time>
+ </trkpt>
+ <trkpt lat="41.634566667" lon="-84.779566667">
+ <ele>306.000</ele>
+ <time>2004-08-17T04:47:39.340Z</time>
+ </trkpt>
+ <trkpt lat="41.630716667" lon="-84.814500000">
+ <ele>295.000</ele>
+ <time>2004-08-17T04:49:31.350Z</time>
+ </trkpt>
+ <trkpt lat="41.629400000" lon="-84.886300000">
+ <ele>330.000</ele>
+ <time>2004-08-17T04:52:58.340Z</time>
+ </trkpt>
+ <trkpt lat="41.632583333" lon="-84.904450000">
+ <ele>317.000</ele>
+ <time>2004-08-17T04:53:57.350Z</time>
+ </trkpt>
+ <trkpt lat="41.631566667" lon="-84.949500000">
+ <ele>314.000</ele>
+ <time>2004-08-17T04:56:20.360Z</time>
+ </trkpt>
+ <trkpt lat="41.634933333" lon="-84.995483333">
+ <ele>322.000</ele>
+ <time>2004-08-17T04:59:13.350Z</time>
+ </trkpt>
+ <trkpt lat="41.634916667" lon="-84.998933333">
+ <ele>332.000</ele>
+ <time>2004-08-17T04:59:34.370Z</time>
+ </trkpt>
+ <trkpt lat="41.634950000" lon="-85.049000000">
+ <ele>328.000</ele>
+ <time>2004-08-17T05:03:33.360Z</time>
+ </trkpt>
+ <trkpt lat="41.604600000" lon="-85.046950000">
+ <ele>304.000</ele>
+ <time>2004-08-17T05:05:34.380Z</time>
+ </trkpt>
+ <trkpt lat="41.598633333" lon="-85.049733333">
+ <ele>297.000</ele>
+ <time>2004-08-17T05:05:57.380Z</time>
+ </trkpt>
+ <trkpt lat="41.591533333" lon="-85.056850000">
+ <ele>296.000</ele>
+ <time>2004-08-17T05:06:29.370Z</time>
+ </trkpt>
+ <trkpt lat="41.584883333" lon="-85.059083333">
+ <ele>311.000</ele>
+ <time>2004-08-17T05:06:54.380Z</time>
+ </trkpt>
+ <trkpt lat="41.421783333" lon="-85.053633333">
+ <ele>285.000</ele>
+ <time>2004-08-17T05:16:40.390Z</time>
+ </trkpt>
+ <trkpt lat="41.354433333" lon="-85.087800000">
+ <ele>276.000</ele>
+ <time>2004-08-17T05:20:59.390Z</time>
+ </trkpt>
+ <trkpt lat="41.257883333" lon="-85.087266667">
+ <ele>275.000</ele>
+ <time>2004-08-17T05:26:41.400Z</time>
+ </trkpt>
+ <trkpt lat="41.248600000" lon="-85.091916667">
+ <ele>271.000</ele>
+ <time>2004-08-17T05:27:16.410Z</time>
+ </trkpt>
+ <trkpt lat="41.239800000" lon="-85.092766667">
+ <ele>266.000</ele>
+ <time>2004-08-17T05:27:47.410Z</time>
+ </trkpt>
+ <trkpt lat="41.232700000" lon="-85.096533333">
+ <ele>258.000</ele>
+ <time>2004-08-17T05:28:14.410Z</time>
+ </trkpt>
+ <trkpt lat="41.224600000" lon="-85.102716667">
+ <ele>265.000</ele>
+ <time>2004-08-17T05:28:47.410Z</time>
+ </trkpt>
+ <trkpt lat="41.216866667" lon="-85.104983333">
+ <ele>267.000</ele>
+ <time>2004-08-17T05:29:15.400Z</time>
+ </trkpt>
+ <trkpt lat="41.180600000" lon="-85.103883333">
+ <ele>258.000</ele>
+ <time>2004-08-17T05:31:24.410Z</time>
+ </trkpt>
+ <trkpt lat="41.179100000" lon="-85.104516667">
+ <ele>261.000</ele>
+ <time>2004-08-17T05:31:35.410Z</time>
+ </trkpt>
+ <trkpt lat="41.177683333" lon="-85.165883333">
+ <ele>264.000</ele>
+ <time>2004-08-17T05:36:07.410Z</time>
+ </trkpt>
+ <trkpt lat="41.155483333" lon="-85.165450000">
+ <ele>265.000</ele>
+ <time>2004-08-17T05:38:02.420Z</time>
+ </trkpt>
+ <trkpt lat="41.155250000" lon="-85.161616667">
+ <ele>262.000</ele>
+ <time>2004-08-17T05:38:28.410Z</time>
+ </trkpt>
+ <trkpt lat="41.154633333" lon="-85.158100000">
+ <ele>259.000</ele>
+ <time>2004-08-17T05:38:58.410Z</time>
+ </trkpt>
+ </trkseg>
+ </trk>
+</gpx>
humminbird_ht ht Humminbird tracks (.ht)
humminbird hwr Humminbird waypoints and routes (.hwr)
lowranceusr usr Lowrance USR
-magellanx upt Magellan SD files (as for eXplorist)
-magellan Magellan SD files (as for Meridian)
-magellan Magellan serial protocol
miniHomer MiniHomer, a skyTraq Venus 6 based logger (download tracks, waypoints and get/set POI)
garmin_xt Mobile Garmin XT Track files
mtk-bin bin MTK Logger (iBlue 747,...) Binary File Format
file humminbird hwr Humminbird waypoints and routes (.hwr)
internal random Internal GPS data generator
file lowranceusr usr Lowrance USR
-file magellanx upt Magellan SD files (as for eXplorist)
-file magellan Magellan SD files (as for Meridian)
-serial magellan Magellan serial protocol
serial miniHomer MiniHomer, a skyTraq Venus 6 based logger (download tracks, waypoints and get/set POI)
file garmin_xt Mobile Garmin XT Track files
file mtk-bin bin MTK Logger (iBlue 747,...) Binary File Format
file rwr-rw humminbird hwr Humminbird waypoints and routes (.hwr)
internal r-r-r- random Internal GPS data generator
file rwrwrw lowranceusr usr Lowrance USR
-file rwrwrw magellanx upt Magellan SD files (as for eXplorist)
-file rwrwrw magellan Magellan SD files (as for Meridian)
-serial rwrwrw magellan Magellan serial protocol
serial r-r--- miniHomer MiniHomer, a skyTraq Venus 6 based logger (download tracks, waypoints and get/set POI)
file --r--- garmin_xt Mobile Garmin XT Track files
file r-r--- mtk-bin bin MTK Logger (iBlue 747,...) Binary File Format
option lowranceusr description (USR output) Output file content description string https://www.gpsbabel.org/WEB_DOC_DIR/fmt_lowranceusr.html#fmt_lowranceusr_o_description
-file rwrwrw magellanx upt Magellan SD files (as for eXplorist) magellanx
- https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellanx.html
-option magellanx deficon Default icon name string https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellanx.html#fmt_magellanx_o_deficon
-
-option magellanx maxcmts Max number of comments to write (maxcmts=200) integer https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellanx.html#fmt_magellanx_o_maxcmts
-
-file rwrwrw magellan Magellan SD files (as for Meridian) magellan
- https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html
-option magellan deficon Default icon name string https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_deficon
-
-option magellan maxcmts Max number of comments to write (maxcmts=200) integer https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_maxcmts
-
-serial rwrwrw magellan Magellan serial protocol magellan
- https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html
-option magellan deficon Default icon name string https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_deficon
-
-option magellan maxcmts Max number of comments to write (maxcmts=200) integer 200 https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_maxcmts
-
-option magellan baud Numeric value of bitrate (baud=4800) integer 4800 https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_baud
-
-option magellan noack Suppress use of handshaking in name of speed boolean https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_noack
-
-option magellan nukewpt Delete all waypoints boolean https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_nukewpt
-
serial r-r--- miniHomer MiniHomer, a skyTraq Venus 6 based logger (download tracks, waypoints and get/set POI) miniHomer
https://www.gpsbabel.org/WEB_DOC_DIR/fmt_miniHomer.html
option miniHomer baud Baud rate used for download integer 115200 0 115200 https://www.gpsbabel.org/WEB_DOC_DIR/fmt_miniHomer.html#fmt_miniHomer_o_baud
title (USR output) Output file title string
serialnum (USR output) Device serial number
description (USR output) Output file content description
- magellanx Magellan SD files (as for eXplorist)
- deficon Default icon name
- maxcmts Max number of comments to write (maxcmts=200)
- magellan Magellan SD files (as for Meridian)
- deficon Default icon name
- maxcmts Max number of comments to write (maxcmts=200)
- magellan Magellan serial protocol
- deficon Default icon name
- maxcmts Max number of comments to write (maxcmts=200)
- baud Numeric value of bitrate (baud=4800)
- noack (0/1) Suppress use of handshaking in name of speed
- nukewpt (0/1) Delete all waypoints
miniHomer MiniHomer, a skyTraq Venus 6 based logger (downloa
baud Baud rate used for download
dump-file Dump raw data to this file
+++ /dev/null
-$PMGNWPL,2805.200,N,08246.200,W,0000000,M,AL7394,872 6833 A TIDAL,a*43\r
-$PMGNWPL,2806.216,N,08246.733,W,0000000,M,AL7485,872 6853 TIDAL 1,a*33\r
-$PMGNWPL,2806.216,N,08246.733,W,0000000,M,AL7484,872 6853 TIDAL 2,a*31\r
-$PMGNWPL,2806.050,N,08246.367,W,0000000,M,AL7482,872 6853 TIDAL 4,a*34\r
-$PMGNWPL,2808.816,N,08245.483,W,0000000,M,AL7471,872 6892 TIDAL 1,a*3A\r
-$PMGNWPL,2808.816,N,08245.483,W,0000000,M,AL7470,872 6892 TIDAL 2,a*38\r
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7380,872 6899 TIDAL 1,a*35\r
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7379,872 6899 TIDAL 2,a*30\r
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7378,872 6899 TIDAL 3,a*30\r
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7377,872 6899 TIDAL 4,a*38\r
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7376,872 6899 TIDAL 5,a*38\r
-$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7386,872 6904 TIDAL 1,a*32\r
-$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7385,872 6904 TIDAL 2,a*32\r
-$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7384,872 6904 TIDAL 3,a*32\r
-$PMGNWPL,2809.216,N,08248.017,W,0000000,M,AL7383,872 6904 TIDAL 4,a*34\r
-$PMGNWPL,2809.216,N,08248.017,W,0000000,M,AL7382,872 6904 TIDAL 5,a*34\r
-$PMGNWPL,2809.617,N,08245.933,W,0000000,M,AL6625,872 6905 TIDAL 5,a*3B\r
-$PMGNWPL,2809.383,N,08244.666,W,0000000,M,AL7392,872 6906 TIDAL 2,a*31\r
+++ /dev/null
-$PMGNWPL,2805.200,N,08246.200,W,0000000,M,AL7394,872 6833 A TIDAL,a*43
-$PMGNWPL,2806.216,N,08246.733,W,0000000,M,AL7485,872 6853 TIDAL 1,a*33
-$PMGNWPL,2806.216,N,08246.733,W,0000000,M,AL7484,872 6853 TIDAL 2,a*31
-$PMGNWPL,2806.050,N,08246.367,W,0000000,M,AL7482,872 6853 TIDAL 4,a*34
-$PMGNWPL,2808.816,N,08245.483,W,0000000,M,AL7471,872 6892 TIDAL 1,a*3A
-$PMGNWPL,2808.816,N,08245.483,W,0000000,M,AL7470,872 6892 TIDAL 2,a*38
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7380,872 6899 TIDAL 1,a*35
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7379,872 6899 TIDAL 2,a*30
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7378,872 6899 TIDAL 3,a*30
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7377,872 6899 TIDAL 4,a*38
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7376,872 6899 TIDAL 5,a*38
-$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7386,872 6904 TIDAL 1,a*32
-$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7385,872 6904 TIDAL 2,a*32
-$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7384,872 6904 TIDAL 3,a*32
-$PMGNWPL,2809.216,N,08248.017,W,0000000,M,AL7383,872 6904 TIDAL 4,a*34
-$PMGNWPL,2809.216,N,08248.017,W,0000000,M,AL7382,872 6904 TIDAL 5,a*34
-$PMGNWPL,2809.617,N,08245.933,W,0000000,M,AL6625,872 6905 TIDAL 5,a*3B
-$PMGNWPL,2809.383,N,08244.666,W,0000000,M,AL7392,872 6906 TIDAL 2,a*31
-$PMGNCMD,END*3D
+++ /dev/null
-$PMGNTRK,3003.731,N,09136.621,W,00001,M,170621.25,A,,250502*6A\r
-$PMGNTRK,3003.767,N,09136.634,W,00000,M,170955.19,A,,250502*6F\r
-$PMGNTRK,3003.762,N,09136.496,W,00000,M,171200.20,A,,250502*60\r
-$PMGNTRK,3003.740,N,09136.443,W,00000,M,171248.75,A,,250502*64\r
-$PMGNTRK,3003.692,N,09136.317,W,00000,M,171441.20,A,,250502*63\r
-$PMGNTRK,3003.587,N,09135.964,W,00000,M,171716.20,A,,250502*68\r
-$PMGNTRK,3003.468,N,09135.801,W,00000,M,171746.20,A,,250502*6F\r
-$PMGNTRK,3003.323,N,09135.694,W,00000,M,171820.81,A,,250502*61\r
-$PMGNTRK,3003.233,N,09135.557,W,00000,M,171901.20,A,,250502*64\r
-$PMGNTRK,3002.984,N,09135.385,W,00000,M,172046.25,A,,250502*67\r
-$PMGNTRK,3002.941,N,09135.393,W,00000,M,172110.25,A,,250502*6B\r
-$PMGNTRK,3002.928,N,09135.576,W,00000,M,172151.37,A,,250502*6F\r
-$PMGNTRK,3002.774,N,09135.787,W,00000,M,172235.20,A,,250502*63\r
-$PMGNTRK,3002.731,N,09135.923,W,00000,M,172308.56,A,,250502*6C\r
-$PMGNTRK,3002.838,N,09136.016,W,00000,M,180423.93,A,,250502*6C\r
-$PMGNTRK,3002.820,N,09135.978,W,00002,M,180604.92,A,,250502*63\r
-$PMGNTRK,3002.786,N,09135.968,W,00000,M,180706.92,A,,250502*60\r
-$PMGNTRK,3002.772,N,09135.937,W,00001,M,180818.92,A,,250502*60\r
-$PMGNTRK,3002.782,N,09135.864,W,00000,M,181020.92,A,,250502*6B\r
-$PMGNTRK,3002.781,N,09135.830,W,00000,M,181109.93,A,,250502*62\r
-$PMGNTRK,3002.807,N,09135.780,W,00000,M,181218.92,A,,250502*65\r
-$PMGNTRK,3002.847,N,09135.712,W,00000,M,181422.93,A,,250502*64\r
-$PMGNTRK,3002.868,N,09135.686,W,00002,M,181504.93,A,,250502*62\r
-$PMGNTRK,3002.895,N,09135.645,W,00001,M,181614.93,A,,250502*6E\r
-$PMGNTRK,3002.921,N,09135.628,W,00001,M,181701.93,A,,250502*6E\r
-$PMGNTRK,3002.961,N,09135.631,W,00000,M,181807.94,A,,250502*6D\r
-$PMGNTRK,3003.019,N,09135.639,W,00002,M,181951.94,A,,250502*62\r
-$PMGNTRK,3003.047,N,09135.647,W,00000,M,182039.94,A,,250502*66\r
-$PMGNTRK,3003.074,N,09135.662,W,00000,M,182124.93,A,,250502*6B\r
-$PMGNTRK,3003.108,N,09135.662,W,00000,M,182217.94,A,,250502*65\r
-$PMGNTRK,3003.133,N,09135.680,W,00000,M,182318.93,A,,250502*68\r
-$PMGNTRK,3003.181,N,09135.681,W,00000,M,182437.94,A,,250502*6D\r
-$PMGNTRK,3003.292,N,09135.712,W,00006,M,182813.95,A,,250502*6A\r
-$PMGNTRK,3003.224,N,09135.696,W,00002,M,183136.94,A,,250502*60\r
-$PMGNTRK,3003.191,N,09135.687,W,00000,M,183256.95,A,,250502*6B\r
-$PMGNTRK,3003.158,N,09135.690,W,00000,M,183402.95,A,,250502*6F\r
-$PMGNTRK,3003.147,N,09135.726,W,00000,M,183603.95,A,,250502*6E\r
-$PMGNTRK,3003.149,N,09135.758,W,00000,M,183648.96,A,,250502*65\r
-$PMGNTRK,3003.159,N,09135.807,W,00001,M,183752.96,A,,250502*6A\r
-$PMGNTRK,3003.188,N,09135.871,W,00000,M,183918.95,A,,250502*65\r
-$PMGNTRK,3003.217,N,09135.878,W,00000,M,184015.96,A,,250502*69\r
-$PMGNTRK,3003.238,N,09135.866,W,00006,M,184125.96,A,,250502*6F\r
-$PMGNTRK,3003.217,N,09135.885,W,00000,M,184237.96,A,,250502*69\r
-$PMGNTRK,3003.192,N,09135.875,W,00000,M,184401.96,A,,250502*6B\r
-$PMGNTRK,3003.169,N,09135.851,W,00000,M,184553.96,A,,250502*6F\r
-$PMGNTRK,3003.154,N,09135.816,W,00000,M,184654.96,A,,250502*66\r
-$PMGNTRK,3003.140,N,09135.786,W,00000,M,184742.97,A,,250502*62\r
-$PMGNTRK,3003.135,N,09135.741,W,00000,M,184841.96,A,,250502*66\r
-$PMGNTRK,3003.133,N,09135.701,W,00000,M,184952.97,A,,250502*66\r
-$PMGNTRK,3003.113,N,09135.682,W,00000,M,185049.97,A,,250502*6C\r
-$PMGNTRK,3003.063,N,09135.664,W,00000,M,185214.97,A,,250502*68\r
-$PMGNTRK,3003.034,N,09135.654,W,00000,M,185256.98,A,,250502*60\r
-$PMGNTRK,3003.011,N,09135.646,W,00000,M,185338.98,A,,250502*6D\r
-$PMGNTRK,3002.946,N,09135.623,W,00000,M,185511.97,A,,250502*66\r
-$PMGNTRK,3002.907,N,09135.655,W,00000,M,185632.98,A,,250502*6F\r
-$PMGNTRK,3002.885,N,09135.685,W,00000,M,185724.97,A,,250502*60\r
-$PMGNTRK,3002.850,N,09135.727,W,00007,M,185840.98,A,,250502*64\r
-$PMGNTRK,3002.824,N,09135.760,W,00000,M,185928.98,A,,250502*6C\r
-$PMGNTRK,3002.798,N,09135.796,W,00000,M,190022.98,A,,250502*6A\r
-$PMGNTRK,3002.784,N,09135.859,W,00000,M,190141.98,A,,250502*6F\r
-$PMGNTRK,3002.774,N,09135.908,W,00000,M,190248.99,A,,250502*6E\r
-$PMGNTRK,3002.779,N,09135.938,W,00000,M,190343.98,A,,250502*6B\r
-$PMGNTRK,3002.807,N,09135.957,W,00000,M,190449.99,A,,250502*68\r
-$PMGNTRK,3002.828,N,09135.980,W,00000,M,190557.98,A,,250502*60\r
+++ /dev/null
-#!/bin/sh
-
-
-# Magellan serial
-# TODO
-
-
-#
-# GPX routes -- since GPX contains a date stamp, tests will always
-# fail, so we use magellan as an interim format...
-#
-rm -f ${TMPDIR}/gpxroute.gpx ${TMPDIR}/maggpx.rte
-gpsbabel -r -i gpx -f ${REFERENCE}/route/route.gpx -o gpx \
- -F ${TMPDIR}/gpxroute.gpx
-gpsbabel -r -i gpx -f ${TMPDIR}/gpxroute.gpx -o magellan \
- -F ${TMPDIR}/maggpx.rte
-compare ${REFERENCE}/route/magellan.rte ${TMPDIR}/maggpx.rte
-
-#
-# GPX tracks -- since GPX contains a date stamp, tests will always
-# fail, so we use magellan as an interim format...
-#
-rm -f ${TMPDIR}/gpxtrack.gpx ${TMPDIR}/maggpx.trk
-gpsbabel -t -i gpx -f ${REFERENCE}/track/tracks.gpx -o gpx \
- -F ${TMPDIR}/gpxtrack.gpx
-gpsbabel -t -i magellan -f ${REFERENCE}/track/meridian.trk -o gpx \
- -F ${TMPDIR}/maggpx.trk
-compare ${TMPDIR}/maggpx.trk ${TMPDIR}/gpxtrack.gpx
-
# XCSV "path distance" test
#
rm -f ${TMPDIR}/pathdist.out
-gpsbabel -i magellan -f ${REFERENCE}/dusky.trk -o xcsv,style=${REFERENCE}/gnuplot.style -F ${TMPDIR}/pathdist.out
+gpsbabel -i gpx -f ${REFERENCE}/dusky.gpx -o xcsv,style=${REFERENCE}/gnuplot.style -F ${TMPDIR}/pathdist.out
compare ${REFERENCE}/dusky.gnuplot ${TMPDIR}/pathdist.out
#
+++ /dev/null
-
-
-#
-# Magellan file format
-#
-gpsbabel -i magellan -f ${REFERENCE}/magfile -o magellan -F ${TMPDIR}/magfile
-compare ${TMPDIR}/magfile ${REFERENCE}/magfile
-
-#
-# Magellanx is just like, but with longer names. (which this admittedly
-# doesn't actually exercise...)
-#
-gpsbabel -i magellan -f ${REFERENCE}/magxfile -o magellanx -F ${TMPDIR}/magxfile
-compare ${TMPDIR}/magxfile ${REFERENCE}/magxfile
-
-# Magellanx routes, however, have an extra 'name' field in them.
-gpsbabel -r -i magellanx -f ${REFERENCE}/route/magexplorist.rte -o magellanx -F ${TMPDIR}/magxfile.rte
-gpsbabel -r -i magellanx -f ${TMPDIR}/magxfile.rte -o magellanx -F ${TMPDIR}/magxfile2.rte
-compare ${REFERENCE}/route/magexplorist.rte ${TMPDIR}/magxfile2.rte
-
-gpsbabel -t -i gpx -f ${REFERENCE}/track/nmeadate.gpx -o magellanx -F ${TMPDIR}/magellandate.log
-compare ${REFERENCE}/track/magellandate.log ${TMPDIR}/magellandate.log
+++ /dev/null
-#
-# magellan SD card waypoint / route format
-#
-rm -f ${TMPDIR}/magellan.rte
-gpsbabel -r -i magellan -f ${REFERENCE}/route/magellan.rte -o magellan \
- -F ${TMPDIR}/magellan.rte
-compare ${REFERENCE}/route/magellan.rte ${TMPDIR}/magellan.rte
-
extern ff_vecs_t geo_vecs;
-extern ff_vecs_t mag_svecs;
-extern ff_vecs_t mag_fvecs;
-extern ff_vecs_t magX_fvecs;
extern ff_vecs_t garmin_vecs;
extern ff_vecs_t ozi_vecs;
#if MAXIMAL_ENABLED
#endif // CSVFMTS_ENABLED
LegacyFormat geo_fmt {geo_vecs};
GpxFormat gpx_fmt;
- LegacyFormat mag_sfmt {mag_svecs};
- LegacyFormat mag_ffmt {mag_fvecs};
- LegacyFormat magX_ffmt {magX_fvecs};
LegacyFormat garmin_fmt {garmin_vecs};
GdbFormat gdb_fmt;
NmeaFormat nmea_fmt;
"gpx",
nullptr,
},
- {
- &mag_sfmt,
- "magellan",
- "Magellan serial protocol",
- nullptr,
- nullptr,
- },
- {
- &mag_ffmt,
- "magellan",
- "Magellan SD files (as for Meridian)",
- nullptr,
- nullptr,
- },
- {
- &magX_ffmt,
- "magellanx",
- "Magellan SD files (as for eXplorist)",
- "upt",
- nullptr,
- },
{
&garmin_fmt,
"garmin",
programs that don't understand it yet and too much data in
alternate formats.</para>
<para>Perhaps you have an
- <link linkend="fmt_magellanx">Explorist 600</link>
+ <link linkend="fmt_garmin">GPSMap 60CSx</link>
and your friend has a
<link linkend="fmt_garmin">StreetPilot 2720</link>
.
transparently with waypoints, tracks, and routes. Some
formats, like
<link linkend="fmt_garmin">garmin</link>
- and
- <link linkend="fmt_magellan">magellan</link>
require the
<parameter>-t</parameter>
flag to work with tracks and
+++ /dev/null
-<para>GPSBabel supports the following <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.magellangps.com">Magellan</link> receivers:
-<simplelist columns="2" type="vert">
- <member>310</member>
- <member>315</member>
- <member>Map330</member>
- <member>SporTrak Map Color</member>
- <member>SporTrak Map</member>
- <member>SporTrak Map Pro</member>
- <member>SporTrak Map Topo</member>
- <member>Meridian (green or yellow)</member>
- <member>Meridian Gold</member>
- <member>Meridian Platinum</member>
- <member>Meridian Color</member>
- <member>Explorist 100 (with aftermarket cable)</member>
- <member>Explorist 200 (with aftermarket cable)</member>
- <member>Explorist 300 (with aftermarket cable)</member>
- <member>Explorist 210</member>
- <member>Explorist 300</member>
- <member>Explorist 400</member>
- <member>Explorist 500</member>
- <member>Explorist 600</member>
- <member>Explorist XL</member>
-</simplelist>
-</para>
-
-<para>
- This format is used for both the serial protocol used on the
- devices with serial ports such as Map330 and Meridian and for
- the files stored either in either the unit's internal memory
- (Explorist 210, Explorist 400, Explorist 500, Explorist 600,
- Explorist XL) or on removable memory.
-</para>
-<para>
- If you specify a serial port for the file (.e.g. "COM1", "/dev/ttyS0")
- to be read or written, GPSBabel will use serial protocol. Specifying
- a file, either on local filesystem or on a mounted flash card reader,
- will results in the file-based format being used.
-</para>
+++ /dev/null
-<para>GPSBabel supports the following <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.magellangps.com">Magellan</link> receivers:
-<simplelist columns="2" type="vert">
- <member>310</member>
- <member>315</member>
- <member>Map330</member>
- <member>SporTrak Map Color</member>
- <member>SporTrak Map</member>
- <member>SporTrak Map Pro</member>
- <member>SporTrak Map Topo</member>
- <member>Meridian (green or yellow)</member>
- <member>Meridian Gold</member>
- <member>Meridian Platinum</member>
- <member>Meridian Color</member>
- <member>Explorist 100 (with aftermarket cable)</member>
- <member>Explorist 200 (with aftermarket cable)</member>
- <member>Explorist 300 (with aftermarket cable)</member>
- <member>Explorist 210</member>
- <member>Explorist 300</member>
- <member>Explorist 400</member>
- <member>Explorist 500</member>
- <member>Explorist 600</member>
- <member>Explorist XL</member>
-</simplelist>
-</para>
-<para>
-The RoadMate family of products is not supported.
-</para>
-
-<para>
- This format is used for both the serial protocol used on the
- devices with serial ports such as Map330 and Meridian and for
- the files stored either in either the unit's internal memory
- (Explorist 210, 400, 500, 600, XL) or on removable memory.
-</para>
-<para>
- If you specify a serial port for the file (.e.g. "COM1", "/dev/ttyS0")
- to be read or written, GPSBabel will use serial protocol. Specifying
- a file, either on local filesystem or on a mounted flash card reader,
- will result in the file-based format being used.
-</para>
-<para>
- Users of the Explorist generation of receivers should probably
- prefer to use the <link linkend="fmt_magellan">magellanx</link>
- format over this one.
-
-</para>
-<important>
-<para>
-This module does not support the units that do not follow Magellan's
-documented communications protocols including:</para>
-<simplelist columns="2" type="vert">
-<member>Maestro 3100</member>
-<member>Maestro 3140</member>
-<member>Maestro 3200</member>
-<member>Maestro 3210</member>
-<member>Maestro 3220</member>
-<member>Maestro 3225</member>
-<member>Maestro 3250</member>
-<member>Maestro 4000</member>
-<member>Maestro 4040</member>
-<member>Maestro 4050</member>
-<member>Maestro 4200</member>
-<member>Maestro 4210</member>
-<member>Maestro 4220</member>
-<member>Maestro 4250</member>
-<member>Maestro 5310</member>
-
-<member> RoadMate 300 </member>
-<member> RoadMate 360 </member>
-<member> RoadMate 500</member>
-<member> RoadMate 700 </member>
-<member> RoadMate 760</member>
-<member> RoadMate 800</member>
-<member> RoadMate 860T</member>
-<member> RoadMate 1200 </member>
-<member> RoadMate 1400 </member>
-<member> RoadMate 1412 </member>
-<member> RoadMate 1430 </member>
-<member> RoadMate 2000 </member>
-<member> RoadMate 2000 </member>
-<member> RoadMate 2200T</member>
-<member> RoadMate 3000T</member>
-<member> RoadMate 3050T</member>
-<member> RoadMate 6000T</member>
-<member> RoadMate AAA </member>
-
-<member> Triton 200 </member>
-<member> Triton 300 </member>
-<member> Triton 400 </member>
-<member> Triton 500 </member>
-<member> Triton 1500 </member>
-<member> Triton 2000 </member>
-
-</simplelist>
-
-</important>
+++ /dev/null
-<para>
- This is the SD card format used by the <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.magellangps.com">Magellan</link> Explorist 400,
- Explorist 500, Explorist 600, and Explorist XL and internally on those devices plus the
- Explorist 210. Stored waypoints are identical to the <link linkend="fmt_magellan">Magellan SD format</link>
- used by Meridian, but the newer models allow longer waypoint names. Routes are
- subtly different.
-</para>
-<para>
- You should name any file containing waypoints created with
- this format with a ".upt" extension so the firmware can read it.
- Similarly, routes should be named ".rte" and tracks should be
- named ".log".
-</para>
+++ /dev/null
-<para>
- The deficon option is used to control the icon output when writing to this format. It overrides any icon information that might be present in the source data.
-</para>
+++ /dev/null
-<para>
-The maxcmts option allows you to specify the number comments that will
-be sent to the unit.
-</para>
-<para>
-Magellan receivers allow a maximum of 200 waypoint comments. Unfortunately,
-DirectRoute uses waypoint comments to provide next turn directions for
-navigation pop-ups and that comes from that pool of 200 comments. It
-is therefore sometimes convenient to limit the number of waypoint
-comments written to the receiver. For example, a geocacher might want
-to upload 400 waypoints, but only 190 with comments so that DirectRoute
-could provide driving directions for the next ten turns.
-</para>
+++ /dev/null
-<para>
- This option causes GPSBabel to use the given baud rate for serial
- communications. It must match the given baud rate on the receiver. The
- default value matches the default on the receiver, 4800.
-</para>
-<para>
- Valid options are 1200, 2400, 4800, 9600, 19200, 57600, and 115200.
-</para>
+++ /dev/null
-<para>
-This option specifies the icon or waypoint type to write for each waypoint on
-output.
-</para>
-<para>
-If this option is specified, its value will be used for all waypoints, not
-just those that do not already have descriptions. That is, this option
-overrides any icon description that might be in the input file.
-</para>
-<!--
-Eventually link to a table of valid icons...
--->
-<para>
-This option has no effect on input.
-</para>
+++ /dev/null
-<para>
-The maxcmts option allows you to specify the number comments that will
-be sent to the unit.
-</para>
-<para>
-Magellan receivers allow a maximum of 200 waypoint comments. Unfortunately,
-DirectRoute uses waypoint comments to provide next turn directions for
-navigation pop-ups and that comes from that pool of 200 comments. It
-is therefore sometimes convenient to limit the number of waypoint
-comments written to the receiver. For example, a geocacher might want
-to upload 400 waypoints, but only 190 with comments so that DirectRoute
-could provide driving directions for the next ten turns.
-</para>
+++ /dev/null
-<para>
-Magellan's protocol specification strongly encourages the use of software
-acknowledgments on every packets. This is a simple "this is what I think
-I heard. If you agree that I heard it correctly, let's go to the next packet"
-handshake that is used to ensure the integrity of the data transfer.
-</para>
-<para>
-Certain firmware versions have problems handling this which makes transfers
-unnecessarily slow. Transfers on all units at high serial speeds are also
-severely restricted by this process.
-</para>
-<para>
-In controlled environments (good cables, low electrical noise, receiving
-from the unit, not doing donuts with the unit set to "track up" at a 150
-mile scale with 500 waypoints on the screen) it is sometimes useful to
-release that safety belt by using the "noack" suboption.
-</para>
+++ /dev/null
-<para>
-This option erases all waypoints in the receiver before doing a transfer.
-</para>
-<para>
-This is a convenient option to use in automated processes where you want
-to ensure the units starts with a clean state before sending waypoints to
-it. Using this option on transmit is a better idea than doing it on
-receive since the latter would erase all the waypoints before asking the
-unit to send all the waypoints.
-</para>
+++ /dev/null
-<para>
- The deficon option is used to control the icon output when writing to this format. It overrides any icon information that might be present in the source data.
-</para>
+++ /dev/null
-<para>
-The maxcmts option allows you to specify the number comments that will
-be sent to the unit.
-</para>
-<para>
-Magellan receivers allow a maximum of 200 waypoint comments. Unfortunately,
-DirectRoute uses waypoint comments to provide next turn directions for
-navigation pop-ups and that comes from that pool of 200 comments. It
-is therefore sometimes convenient to limit the number of waypoint
-comments written to the receiver. For example, a geocacher might want
-to upload 400 waypoints, but only 190 with comments so that DirectRoute
-could provide driving directions for the next ten turns.
-</para>